removed pro and bundle nag screens

This commit is contained in:
Eric 2024-08-30 20:02:15 -07:00
parent 64c6613c23
commit 5b778890cf
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
9 changed files with 50 additions and 638 deletions

56
.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": "512c99d6d32d3f7ca27de9d00b3f6bee",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "407a6d564f35c7db571702ef6996adc1",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "03c278694182a2a7cd6e39d7cf0cdde8",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "8c0f3132135f6a392b31dccce44ecc72",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "399783ec75c825a7c0ceffb6b66546a6",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "7d5f43f891ddbc0e68e1e4b924717941",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "97b462045118ca81436452c19e50ead3",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "daedf2d4f2c7012f9f1146ef5e675a51",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "ef571b31a6175b391c70cde46587afc6",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "fcbe9cb15740f5569d32daaef838bae3",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "06d8ed9ca06ed1fb12437708b117621f",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "eff858f25a173f090731401c42fcfad7",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "21401c4a1bb13c6a50fbcc2413a34bc6",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "a5f6bf1f1a675a60b43674cbe06ee740",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "4965d74db093926f890d37a2d2bcb0cf",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "527d364caa4c68cb3a5e70575a834333",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "f3e69ff8d4928b52fb301c911d6e01cb",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "82dd100621de502523d9feca94de7fc5",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "6c66e68c95330200d339ec5a44706491",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "f6903fb7071440ee807963cf34fd220f",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "d6246c930e7e2d2d9a6aff6788f33b69",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "001c67c4d4d33e20755399e0b2ed1593",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "6f6bfaf19daf6e866f4fecbc889b8854",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "ebc7449903d7868c631c504aed10f371",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "91f03e7dbfc0d7eb75568704f681fba5",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "0b32b6eb05df0a7e23e55ff2e7235a8f",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "4ed4b63cc506815f759412a295bfe088",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "1890731fb8a2fb43c4cde72af9d7a4e6",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "1a4764a504dcb20591ac3472c48db8d9",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "9bfd6234aac4ffd7d7601a40017a73be",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "941f8fb79d54522ca5df97a735c3babe",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "290a3d9840efc5e88532e13b76b3ae6b",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "621a6684b54b0a9101f808209bcca1ee",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "43c8b0b29e5fb257e4cde0f7fce1c680",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "9a30c6b88ffc11bdf6765780616d8ba1",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "8a8f63850fc296b060a994f4e001d74f",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "2864e9ae1d2d566def85f1cdf6e863fe",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "dc0db7ca99661a896634fc05187a5c75",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "f56bb0fafe0a45cecdcd06a10a6924c9",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "e3a58b09fea193d78187f97cf922717d",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "73ad3303fe1a82005918fbc5dae3446c",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "fa659b5d6119acba6570c92ce4d35ae2",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "73ad3303fe1a82005918fbc5dae3446c",
@ -4132,14 +4132,14 @@
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "3e5c5fd0a09f55ba7b05ce1e2ec7171e",
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "1659535e95e3047fda529543e265ac97",
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "3e5c5fd0a09f55ba7b05ce1e2ec7171e",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "3193670862ed84af2f6bbf3dc7265137",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "5e45916a6cfb40a5667c691e507ea81d",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "419fba17d012361c3ca8174c44dffdb2",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "d895b4afb3bb74dd1c94595a126a2c24",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "56a67ab9520c07bc4be68162e78534b3",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "40f942e6da490173c0e17fdf23e786e8",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "3242b64140d0b0d6f7af09825d9489e0",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "5d41c8b29dc4e585b878c100e01e8af4",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "6b6aae30362b1e9aaf3f1a8daae0b0b4",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "25db08ee2b4c79ca631e517b622c741d",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "2ea419644782c4c52d1fa784d0ced86d",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "c40627a46ee127997153df041e47eda5",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "fff81b29e2d86031ec25c4a672a243e7",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "2a87e2f53c658d0be3918476cd8a04ee",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "1e91536979b318b9a8325ab0e871ecec",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "c6506399a93ef93e8322425f78e2e6b0",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "cb299985623bbcc86015cb103a424ae6",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "efa61468cf098f77cc6a234461d8b86d",

View File

@ -1,4 +1,4 @@
### 1.7.37 (build 21976, api 9, 2024-08-30)
### 1.7.37 (build 21977, api 9, 2024-08-30)
- 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.
@ -8,6 +8,7 @@
- Soundtrack customization no longer requires pro.
- 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.
- 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

@ -399,7 +399,6 @@
"ba_data/python/bauiv1lib/__pycache__/resourcetypeinfo.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/sendinfo.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/serverdialog.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/specialoffer.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/tabs.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/teamnamescolors.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/tournamententry.cpython-312.opt-1.pyc",
@ -558,7 +557,6 @@
"ba_data/python/bauiv1lib/soundtrack/edit.py",
"ba_data/python/bauiv1lib/soundtrack/entrytypeselect.py",
"ba_data/python/bauiv1lib/soundtrack/macmusicapp.py",
"ba_data/python/bauiv1lib/specialoffer.py",
"ba_data/python/bauiv1lib/store/__init__.py",
"ba_data/python/bauiv1lib/store/__pycache__/__init__.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/store/__pycache__/browser.cpython-312.opt-1.pyc",

View File

@ -425,7 +425,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bauiv1lib/soundtrack/edit.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/soundtrack/entrytypeselect.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/soundtrack/macmusicapp.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/specialoffer.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__init__.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/browser.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/button.py \
@ -704,7 +703,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bauiv1lib/soundtrack/__pycache__/edit.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/soundtrack/__pycache__/entrytypeselect.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/soundtrack/__pycache__/macmusicapp.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/specialoffer.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__pycache__/__init__.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__pycache__/browser.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__pycache__/button.cpython-312.opt-1.pyc \

View File

@ -216,24 +216,6 @@ class ClassicAppSubsystem(babase.AppSubsystem):
cfg['launchCount'] = launch_count
cfg.commit()
# Run a test in a few seconds to see if we should pop up an existing
# pending special offer.
def check_special_offer() -> None:
assert plus is not None
from bauiv1lib.specialoffer import show_offer
if (
'pendingSpecialOffer' in cfg
and plus.get_v1_account_public_login_id()
== cfg['pendingSpecialOffer']['a']
):
self.special_offer = cfg['pendingSpecialOffer']['o']
show_offer()
if babase.app.env.gui:
babase.apptimer(3.0, check_special_offer)
# If there's a leftover log file, attempt to upload it to the
# master-server and/or get rid of it.
babase.handle_leftover_v1_cloud_log_file()
@ -832,7 +814,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
app = bauiv1.app
env = app.env
with bascenev1.ContextRef.empty():
from bauiv1lib import specialoffer
# from bauiv1lib import specialoffer
assert app.classic is not None
if app.env.headless:
@ -927,11 +909,11 @@ class ClassicAppSubsystem(babase.AppSubsystem):
# (we may not have heard back from the server)
# ..if that doesn't work they'll just have to wait
# until the next opportunity.
if not specialoffer.show_offer():
# if not specialoffer.show_offer():
def try_again() -> None:
if not specialoffer.show_offer():
# Try one last time..
bauiv1.apptimer(2.0, specialoffer.show_offer)
# def try_again() -> None:
# if not specialoffer.show_offer():
# # Try one last time..
# bauiv1.apptimer(2.0, specialoffer.show_offer)
bauiv1.apptimer(2.0, try_again)
# bauiv1.apptimer(2.0, try_again)

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 = 21976
TARGET_BALLISTICA_BUILD = 21977
TARGET_BALLISTICA_VERSION = '1.7.37'

View File

@ -207,20 +207,22 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
)
def _ui_menu(self) -> None:
from bauiv1lib import specialoffer
# from bauiv1lib import specialoffer
# if specialoffer.show_offer():
# return
if specialoffer.show_offer():
return
bui.containerwidget(edit=self._root_ui, transition='out_left')
with self.context:
bs.timer(0.1, bs.Call(bs.WeakCall(self.session.end)))
def _ui_restart(self) -> None:
from bauiv1lib.tournamententry import TournamentEntryWindow
from bauiv1lib import specialoffer
if specialoffer.show_offer():
return
# from bauiv1lib import specialoffer
# if specialoffer.show_offer():
# return
# If we're in a tournament and it looks like there's no time left,
# disallow.
@ -268,10 +270,10 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
self.end({'outcome': 'restart'})
def _ui_next(self) -> None:
from bauiv1lib.specialoffer import show_offer
# from bauiv1lib.specialoffer import show_offer
if show_offer():
return
# if show_offer():
# return
# If we didn't just complete this level but are choosing to play the
# next one, set it as current (this won't happen otherwise).

View File

@ -1,569 +0,0 @@
# Released under the MIT License. See LICENSE for details.
#
"""UI for presenting sales/etc."""
from __future__ import annotations
import copy
import logging
from typing import TYPE_CHECKING
import bauiv1 as bui
if TYPE_CHECKING:
from typing import Any
class SpecialOfferWindow(bui.Window):
"""Window for presenting sales/etc."""
def __init__(self, offer: dict[str, Any], transition: str = 'in_right'):
# pylint: disable=too-many-statements
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals
from babase import SpecialChar
from bauiv1lib.store import item as storeitemui
plus = bui.app.plus
assert plus is not None
assert bui.app.classic is not None
store = bui.app.classic.store
self._cancel_delay = offer.get('cancelDelay', 0)
# First thing: if we're offering pro or an IAP, see if we have a
# price for it.
# If not, abort and go into zombie mode (the user should never see
# us that way).
real_price: str | None
# Misnomer: 'pro' actually means offer 'pro_sale'.
if offer['item'] in ['pro', 'pro_fullprice']:
real_price = plus.get_price(
'pro' if offer['item'] == 'pro_fullprice' else 'pro_sale'
)
if real_price is None and bui.app.env.debug:
print('NOTE: Faking prices for debug build.')
real_price = '$1.23'
zombie = real_price is None
elif isinstance(offer['price'], str):
# (a string price implies IAP id)
real_price = plus.get_price(offer['price'])
if real_price is None and bui.app.env.debug:
print('NOTE: Faking price for debug build.')
real_price = '$1.23'
zombie = real_price is None
else:
real_price = None
zombie = False
if real_price is None:
real_price = '?'
if offer['item'] in ['pro', 'pro_fullprice']:
self._offer_item = 'pro'
else:
self._offer_item = offer['item']
# If we wanted a real price but didn't find one, go zombie.
if zombie:
return
# This can pop up suddenly, so lets block input for 1 second.
bui.lock_all_input()
bui.apptimer(1.0, bui.unlock_all_input)
bui.getsound('ding').play()
bui.apptimer(0.3, bui.getsound('ooh').play)
self._offer = copy.deepcopy(offer)
self._width = 580
self._height = 590
uiscale = bui.app.ui_v1.uiscale
super().__init__(
root_widget=bui.containerwidget(
size=(self._width, self._height),
transition=transition,
scale=(
1.2
if uiscale is bui.UIScale.SMALL
else 1.15 if uiscale is bui.UIScale.MEDIUM else 1.0
),
stack_offset=(
(0, -15) if uiscale is bui.UIScale.SMALL else (0, 0)
),
)
)
self._is_bundle_sale = False
try:
if offer['item'] in ['pro', 'pro_fullprice']:
original_price_str = plus.get_price('pro')
if original_price_str is None:
original_price_str = '?'
new_price_str = plus.get_price('pro_sale')
if new_price_str is None:
new_price_str = '?'
percent_off_text = ''
else:
# If the offer includes bonus tickets, it's a bundle-sale.
if (
'bonusTickets' in offer
and offer['bonusTickets'] is not None
):
self._is_bundle_sale = True
original_price = plus.get_v1_account_misc_read_val(
'price.' + self._offer_item, 9999
)
# For pure ticket prices we can show a percent-off.
if isinstance(offer['price'], int):
new_price = offer['price']
tchar = bui.charstr(SpecialChar.TICKET)
original_price_str = tchar + str(original_price)
new_price_str = tchar + str(new_price)
percent_off = int(
round(
100.0 - (float(new_price) / original_price) * 100.0
)
)
percent_off_text = ' ' + bui.Lstr(
resource='store.salePercentText'
).evaluate().replace('${PERCENT}', str(percent_off))
else:
original_price_str = new_price_str = '?'
percent_off_text = ''
except Exception:
logging.exception('Error setting up special-offer: %s.', offer)
original_price_str = new_price_str = '?'
percent_off_text = ''
# If its a bundle sale, change the title.
if self._is_bundle_sale:
sale_text = bui.Lstr(
resource='store.saleBundleText',
fallback_resource='store.saleText',
).evaluate()
else:
# For full pro we say 'Upgrade?' since its not really a sale.
if offer['item'] == 'pro_fullprice':
sale_text = bui.Lstr(
resource='store.upgradeQuestionText',
fallback_resource='store.saleExclaimText',
).evaluate()
else:
sale_text = bui.Lstr(
resource='store.saleExclaimText',
fallback_resource='store.saleText',
).evaluate()
self._title_text = bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height - 40),
size=(0, 0),
text=sale_text
+ (
(' ' + bui.Lstr(resource='store.oneTimeOnlyText').evaluate())
if self._offer['oneTimeOnly']
else ''
)
+ percent_off_text,
h_align='center',
v_align='center',
maxwidth=self._width * 0.9 - 220,
scale=1.4,
color=(0.3, 1, 0.3),
)
self._flash_on = False
self._flashing_timer: bui.AppTimer | None = bui.AppTimer(
0.05, bui.WeakCall(self._flash_cycle), repeat=True
)
bui.apptimer(0.6, bui.WeakCall(self._stop_flashing))
size = store.get_store_item_display_size(self._offer_item)
display: dict[str, Any] = {}
storeitemui.instantiate_store_item_display(
self._offer_item,
display,
parent_widget=self._root_widget,
b_pos=(
self._width * 0.5
- size[0] * 0.5
+ 10
- ((size[0] * 0.5 + 30) if self._is_bundle_sale else 0),
self._height * 0.5
- size[1] * 0.5
+ 20
+ (20 if self._is_bundle_sale else 0),
),
b_width=size[0],
b_height=size[1],
button=not self._is_bundle_sale,
)
# Wire up the parts we need.
if self._is_bundle_sale:
self._plus_text = bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height * 0.5 + 50),
size=(0, 0),
text='+',
h_align='center',
v_align='center',
maxwidth=self._width * 0.9,
scale=1.4,
color=(0.5, 0.5, 0.5),
)
self._plus_tickets = bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5 + 120, self._height * 0.5 + 50),
size=(0, 0),
text=bui.charstr(SpecialChar.TICKET_BACKING)
+ str(offer['bonusTickets']),
h_align='center',
v_align='center',
maxwidth=self._width * 0.9,
scale=2.5,
color=(0.2, 1, 0.2),
)
self._price_text = bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, 150),
size=(0, 0),
text=real_price,
h_align='center',
v_align='center',
maxwidth=self._width * 0.9,
scale=1.4,
color=(0.2, 1, 0.2),
)
# Total-value if they supplied it.
total_worth_item = offer.get('valueItem', None)
if total_worth_item is not None:
price = plus.get_price(total_worth_item)
total_worth_price = (
store.get_clean_price(price) if price is not None else None
)
if total_worth_price is not None:
total_worth_text = bui.Lstr(
resource='store.totalWorthText',
subs=[('${TOTAL_WORTH}', total_worth_price)],
)
self._total_worth_text = bui.textwidget(
parent=self._root_widget,
text=total_worth_text,
position=(self._width * 0.5, 210),
scale=0.9,
maxwidth=self._width * 0.7,
size=(0, 0),
h_align='center',
v_align='center',
shadow=1.0,
flatness=1.0,
color=(0.3, 1, 1),
)
elif offer['item'] == 'pro_fullprice':
# for full-price pro we simply show full price
bui.textwidget(edit=display['price_widget'], text=real_price)
bui.buttonwidget(
edit=display['button'], on_activate_call=self._purchase
)
else:
# Show old/new prices otherwise (for pro sale).
bui.buttonwidget(
edit=display['button'], on_activate_call=self._purchase
)
bui.imagewidget(edit=display['price_slash_widget'], opacity=1.0)
bui.textwidget(
edit=display['price_widget_left'], text=original_price_str
)
bui.textwidget(
edit=display['price_widget_right'], text=new_price_str
)
# Add ticket button only if this is ticket-purchasable.
# if isinstance(offer.get('price'), int):
# self._get_tickets_button = bui.buttonwidget(
# parent=self._root_widget,
# position=(self._width - 125, self._height - 68),
# size=(90, 55),
# scale=1.0,
# button_type='square',
# color=(0.7, 0.5, 0.85),
# textcolor=(0.2, 1, 0.2),
# autoselect=True,
# label=bui.Lstr(resource='getTicketsWindow.titleText'),
# on_activate_call=self._on_get_more_tickets_press,
# )
# self._ticket_text_update_timer = bui.AppTimer(
# 1.0, bui.WeakCall(self._update_tickets_text), repeat=True
# )
# self._update_tickets_text()
self._update_timer = bui.AppTimer(
1.0, bui.WeakCall(self._update), repeat=True
)
self._cancel_button = bui.buttonwidget(
parent=self._root_widget,
position=(
(50, 40)
if self._is_bundle_sale
else (self._width * 0.5 - 75, 40)
),
size=(150, 60),
scale=1.0,
on_activate_call=self._cancel,
autoselect=True,
label=bui.Lstr(resource='noThanksText'),
)
self._cancel_countdown_text = bui.textwidget(
parent=self._root_widget,
text='',
position=(
(50 + 150 + 20, 40 + 27)
if self._is_bundle_sale
else (self._width * 0.5 - 75 + 150 + 20, 40 + 27)
),
scale=1.1,
size=(0, 0),
h_align='left',
v_align='center',
shadow=1.0,
flatness=1.0,
color=(0.6, 0.5, 0.5),
)
self._update_cancel_button_graphics()
if self._is_bundle_sale:
self._purchase_button = bui.buttonwidget(
parent=self._root_widget,
position=(self._width - 200, 40),
size=(150, 60),
scale=1.0,
on_activate_call=self._purchase,
autoselect=True,
label=bui.Lstr(resource='store.purchaseText'),
)
bui.containerwidget(
edit=self._root_widget,
cancel_button=self._cancel_button,
start_button=(
self._purchase_button if self._is_bundle_sale else None
),
selected_child=(
self._purchase_button
if self._is_bundle_sale
else display['button']
),
)
def _stop_flashing(self) -> None:
self._flashing_timer = None
bui.textwidget(edit=self._title_text, color=(0.3, 1, 0.3))
def _flash_cycle(self) -> None:
if not self._root_widget:
return
self._flash_on = not self._flash_on
bui.textwidget(
edit=self._title_text,
color=(0.3, 1, 0.3) if self._flash_on else (1, 0.5, 0),
)
def _update_cancel_button_graphics(self) -> None:
bui.buttonwidget(
edit=self._cancel_button,
color=(
(0.5, 0.5, 0.5) if self._cancel_delay > 0 else (0.7, 0.4, 0.34)
),
textcolor=(
(0.5, 0.5, 0.5) if self._cancel_delay > 0 else (0.9, 0.9, 1.0)
),
)
bui.textwidget(
edit=self._cancel_countdown_text,
text=str(self._cancel_delay) if self._cancel_delay > 0 else '',
)
def _update(self) -> None:
plus = bui.app.plus
assert plus is not None
# If we've got seconds left on our countdown, update it.
if self._cancel_delay > 0:
self._cancel_delay = max(0, self._cancel_delay - 1)
self._update_cancel_button_graphics()
can_die = False
# We go away if we see that our target item is owned.
if self._offer_item == 'pro':
assert bui.app.classic is not None
if bui.app.classic.accounts.have_pro():
can_die = True
else:
if plus.get_purchased(self._offer_item):
can_die = True
if can_die:
self._transition_out('out_left')
def _transition_out(self, transition: str = 'out_left') -> None:
# Also clear any pending-special-offer we've stored at this point.
cfg = bui.app.config
if 'pendingSpecialOffer' in cfg:
del cfg['pendingSpecialOffer']
cfg.commit()
bui.containerwidget(edit=self._root_widget, transition=transition)
# def _update_tickets_text(self) -> None:
# from babase import SpecialChar
# plus = bui.app.plus
# assert plus is not None
# if not self._root_widget:
# return
# sval: str | bui.Lstr
# if plus.get_v1_account_state() == 'signed_in':
# sval = bui.charstr(SpecialChar.TICKET) + str(
# plus.get_v1_account_ticket_count()
# )
# else:
# sval = bui.Lstr(resource='getTicketsWindow.titleText')
# bui.buttonwidget(edit=self._get_tickets_button, label=sval)
# def _on_get_more_tickets_press(self) -> None:
# from bauiv1lib import account
# from bauiv1lib import gettickets
# plus = bui.app.plus
# assert plus is not None
# if plus.get_v1_account_state() != 'signed_in':
# account.show_sign_in_prompt()
# return
# gettickets.GetTicketsWindow(modal=True).get_root_widget()
def _purchase(self) -> None:
# from bauiv1lib import gettickets
from bauiv1lib import confirm
plus = bui.app.plus
assert plus is not None
assert bui.app.classic is not None
store = bui.app.classic.store
if self._offer['item'] == 'pro':
plus.purchase('pro_sale')
elif self._offer['item'] == 'pro_fullprice':
plus.purchase('pro')
elif self._is_bundle_sale:
# With bundle sales, the price is the name of the IAP.
plus.purchase(self._offer['price'])
else:
ticket_count: int | None
try:
ticket_count = plus.get_v1_account_ticket_count()
except Exception:
ticket_count = None
if ticket_count is not None and ticket_count < self._offer['price']:
# gettickets.show_get_tickets_prompt()
bui.getsound('error').play()
return
def do_it() -> None:
assert plus is not None
plus.in_game_purchase(
'offer:' + str(self._offer['id']), self._offer['price']
)
bui.getsound('swish').play()
confirm.ConfirmWindow(
bui.Lstr(
resource='store.purchaseConfirmText',
subs=[
(
'${ITEM}',
store.get_store_item_name_translated(
self._offer['item']
),
)
],
),
width=400,
height=120,
action=do_it,
ok_text=bui.Lstr(
resource='store.purchaseText', fallback_resource='okText'
),
)
def _cancel(self) -> None:
if self._cancel_delay > 0:
bui.getsound('error').play()
return
self._transition_out('out_right')
def show_offer() -> bool:
"""(internal)"""
try:
from bauiv1lib import feedback
plus = bui.app.plus
assert plus is not None
app = bui.app
if app.classic is None:
raise RuntimeError(
'Classic feature-set is required to show offers.'
)
# Space things out a bit so we don't hit the poor user with an
# ad and then an in-game offer.
has_been_long_enough_since_ad = True
if app.classic.ads.last_ad_completion_time is not None and (
bui.apptime() - app.classic.ads.last_ad_completion_time < 30.0
):
has_been_long_enough_since_ad = False
if (
app.classic.special_offer is not None
and has_been_long_enough_since_ad
):
# Special case: for pro offers, store this in our prefs so
# we can re-show it if the user kills us (set phasers to
# 'NAG'!!!).
if app.classic.special_offer.get('item') == 'pro_fullprice':
cfg = app.config
cfg['pendingSpecialOffer'] = {
'a': plus.get_v1_account_public_login_id(),
'o': app.classic.special_offer,
}
cfg.commit()
if app.classic.special_offer['item'] == 'rating':
# Go with a native thing if we've got one.
if bui.native_review_request_supported():
bui.native_review_request()
else:
if app.ui_v1.available:
feedback.ask_for_rating()
else:
if app.ui_v1.available:
SpecialOfferWindow(app.classic.special_offer)
app.classic.special_offer = None
return True
except Exception:
logging.exception('Error showing offer.')
return False

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 = 21976;
const int kEngineBuildNumber = 21977;
const char* kEngineVersion = "1.7.37";
const int kEngineApiVersion = 9;