mirror of
https://github.com/hypervortex/VH-Bombsquad-Modded-Server-Files
synced 2025-11-07 17:36:08 +00:00
789 lines
26 KiB
Python
789 lines
26 KiB
Python
|
|
# Released under the MIT License. See LICENSE for details.
|
||
|
|
#
|
||
|
|
"""UI functionality for purchasing/acquiring currency."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from typing import TYPE_CHECKING
|
||
|
|
|
||
|
|
import ba
|
||
|
|
import ba.internal
|
||
|
|
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
|
||
|
|
class GetCurrencyWindow(ba.Window):
|
||
|
|
"""Window for purchasing/acquiring currency."""
|
||
|
|
|
||
|
|
def __init__(
|
||
|
|
self,
|
||
|
|
transition: str = 'in_right',
|
||
|
|
from_modal_store: bool = False,
|
||
|
|
modal: bool = False,
|
||
|
|
origin_widget: ba.Widget | None = None,
|
||
|
|
store_back_location: str | None = None,
|
||
|
|
):
|
||
|
|
# pylint: disable=too-many-statements
|
||
|
|
# pylint: disable=too-many-locals
|
||
|
|
|
||
|
|
ba.set_analytics_screen('Get Tickets Window')
|
||
|
|
|
||
|
|
self._transitioning_out = False
|
||
|
|
self._store_back_location = store_back_location # ew.
|
||
|
|
|
||
|
|
self._ad_button_greyed = False
|
||
|
|
self._smooth_update_timer: ba.Timer | None = None
|
||
|
|
self._ad_button = None
|
||
|
|
self._ad_label = None
|
||
|
|
self._ad_image = None
|
||
|
|
self._ad_time_text = None
|
||
|
|
|
||
|
|
# If they provided an origin-widget, scale up from that.
|
||
|
|
scale_origin: tuple[float, float] | None
|
||
|
|
if origin_widget is not None:
|
||
|
|
self._transition_out = 'out_scale'
|
||
|
|
scale_origin = origin_widget.get_screen_space_center()
|
||
|
|
transition = 'in_scale'
|
||
|
|
else:
|
||
|
|
self._transition_out = 'out_right'
|
||
|
|
scale_origin = None
|
||
|
|
|
||
|
|
uiscale = ba.app.ui.uiscale
|
||
|
|
self._width = 1000.0 if uiscale is ba.UIScale.SMALL else 800.0
|
||
|
|
x_inset = 100.0 if uiscale is ba.UIScale.SMALL else 0.0
|
||
|
|
self._height = 480.0
|
||
|
|
|
||
|
|
self._modal = modal
|
||
|
|
self._from_modal_store = from_modal_store
|
||
|
|
self._r = 'getTicketsWindow'
|
||
|
|
|
||
|
|
top_extra = 20 if uiscale is ba.UIScale.SMALL else 0
|
||
|
|
|
||
|
|
super().__init__(
|
||
|
|
root_widget=ba.containerwidget(
|
||
|
|
size=(self._width, self._height + top_extra),
|
||
|
|
transition=transition,
|
||
|
|
scale_origin_stack_offset=scale_origin,
|
||
|
|
color=(0.4, 0.37, 0.55),
|
||
|
|
scale=(
|
||
|
|
1.63
|
||
|
|
if uiscale is ba.UIScale.SMALL
|
||
|
|
else 1.2
|
||
|
|
if uiscale is ba.UIScale.MEDIUM
|
||
|
|
else 1.0
|
||
|
|
),
|
||
|
|
stack_offset=(0, -3) if uiscale is ba.UIScale.SMALL else (0, 0),
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
btn = ba.buttonwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
position=(55 + x_inset, self._height - 79),
|
||
|
|
size=(140, 60),
|
||
|
|
scale=1.0,
|
||
|
|
autoselect=True,
|
||
|
|
label=ba.Lstr(resource='doneText' if modal else 'backText'),
|
||
|
|
button_type='regular' if modal else 'back',
|
||
|
|
on_activate_call=self._back,
|
||
|
|
)
|
||
|
|
|
||
|
|
ba.containerwidget(edit=self._root_widget, cancel_button=btn)
|
||
|
|
|
||
|
|
ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
position=(self._width * 0.5, self._height - 55),
|
||
|
|
size=(0, 0),
|
||
|
|
color=ba.app.ui.title_color,
|
||
|
|
scale=1.2,
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
text=ba.Lstr(resource=self._r + '.titleText'),
|
||
|
|
maxwidth=290,
|
||
|
|
)
|
||
|
|
|
||
|
|
if not modal:
|
||
|
|
ba.buttonwidget(
|
||
|
|
edit=btn,
|
||
|
|
button_type='backSmall',
|
||
|
|
size=(60, 60),
|
||
|
|
label=ba.charstr(ba.SpecialChar.BACK),
|
||
|
|
)
|
||
|
|
|
||
|
|
b_size = (220.0, 180.0)
|
||
|
|
v = self._height - b_size[1] - 80
|
||
|
|
spacing = 1
|
||
|
|
|
||
|
|
self._ad_button = None
|
||
|
|
|
||
|
|
def _add_button(
|
||
|
|
item: str,
|
||
|
|
position: tuple[float, float],
|
||
|
|
size: tuple[float, float],
|
||
|
|
label: ba.Lstr,
|
||
|
|
price: str | None = None,
|
||
|
|
tex_name: str | None = None,
|
||
|
|
tex_opacity: float = 1.0,
|
||
|
|
tex_scale: float = 1.0,
|
||
|
|
enabled: bool = True,
|
||
|
|
text_scale: float = 1.0,
|
||
|
|
) -> ba.Widget:
|
||
|
|
btn2 = ba.buttonwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
position=position,
|
||
|
|
button_type='square',
|
||
|
|
size=size,
|
||
|
|
label='',
|
||
|
|
autoselect=True,
|
||
|
|
color=None if enabled else (0.5, 0.5, 0.5),
|
||
|
|
on_activate_call=(
|
||
|
|
ba.Call(self._purchase, item)
|
||
|
|
if enabled
|
||
|
|
else self._disabled_press
|
||
|
|
),
|
||
|
|
)
|
||
|
|
txt = ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
text=label,
|
||
|
|
position=(
|
||
|
|
position[0] + size[0] * 0.5,
|
||
|
|
position[1] + size[1] * 0.3,
|
||
|
|
),
|
||
|
|
scale=text_scale,
|
||
|
|
maxwidth=size[0] * 0.75,
|
||
|
|
size=(0, 0),
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
draw_controller=btn2,
|
||
|
|
color=(0.7, 0.9, 0.7, 1.0 if enabled else 0.2),
|
||
|
|
)
|
||
|
|
if price is not None and enabled:
|
||
|
|
ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
text=price,
|
||
|
|
position=(
|
||
|
|
position[0] + size[0] * 0.5,
|
||
|
|
position[1] + size[1] * 0.17,
|
||
|
|
),
|
||
|
|
scale=0.7,
|
||
|
|
maxwidth=size[0] * 0.75,
|
||
|
|
size=(0, 0),
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
draw_controller=btn2,
|
||
|
|
color=(0.4, 0.9, 0.4, 1.0),
|
||
|
|
)
|
||
|
|
i = None
|
||
|
|
if tex_name is not None:
|
||
|
|
tex_size = 90.0 * tex_scale
|
||
|
|
i = ba.imagewidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
texture=ba.gettexture(tex_name),
|
||
|
|
position=(
|
||
|
|
position[0] + size[0] * 0.5 - tex_size * 0.5,
|
||
|
|
position[1] + size[1] * 0.66 - tex_size * 0.5,
|
||
|
|
),
|
||
|
|
size=(tex_size, tex_size),
|
||
|
|
draw_controller=btn2,
|
||
|
|
opacity=tex_opacity * (1.0 if enabled else 0.25),
|
||
|
|
)
|
||
|
|
if item == 'ad':
|
||
|
|
self._ad_button = btn2
|
||
|
|
self._ad_label = txt
|
||
|
|
assert i is not None
|
||
|
|
self._ad_image = i
|
||
|
|
self._ad_time_text = ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
text='1m 10s',
|
||
|
|
position=(
|
||
|
|
position[0] + size[0] * 0.5,
|
||
|
|
position[1] + size[1] * 0.5,
|
||
|
|
),
|
||
|
|
scale=text_scale * 1.2,
|
||
|
|
maxwidth=size[0] * 0.85,
|
||
|
|
size=(0, 0),
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
draw_controller=btn2,
|
||
|
|
color=(0.4, 0.9, 0.4, 1.0),
|
||
|
|
)
|
||
|
|
return btn2
|
||
|
|
|
||
|
|
rsrc = self._r + '.ticketsText'
|
||
|
|
|
||
|
|
c2txt = ba.Lstr(
|
||
|
|
resource=rsrc,
|
||
|
|
subs=[
|
||
|
|
(
|
||
|
|
'${COUNT}',
|
||
|
|
str(
|
||
|
|
ba.internal.get_v1_account_misc_read_val(
|
||
|
|
'tickets2Amount', 500
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
],
|
||
|
|
)
|
||
|
|
c3txt = ba.Lstr(
|
||
|
|
resource=rsrc,
|
||
|
|
subs=[
|
||
|
|
(
|
||
|
|
'${COUNT}',
|
||
|
|
str(
|
||
|
|
ba.internal.get_v1_account_misc_read_val(
|
||
|
|
'tickets3Amount', 1500
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
],
|
||
|
|
)
|
||
|
|
c4txt = ba.Lstr(
|
||
|
|
resource=rsrc,
|
||
|
|
subs=[
|
||
|
|
(
|
||
|
|
'${COUNT}',
|
||
|
|
str(
|
||
|
|
ba.internal.get_v1_account_misc_read_val(
|
||
|
|
'tickets4Amount', 5000
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
],
|
||
|
|
)
|
||
|
|
c5txt = ba.Lstr(
|
||
|
|
resource=rsrc,
|
||
|
|
subs=[
|
||
|
|
(
|
||
|
|
'${COUNT}',
|
||
|
|
str(
|
||
|
|
ba.internal.get_v1_account_misc_read_val(
|
||
|
|
'tickets5Amount', 15000
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
],
|
||
|
|
)
|
||
|
|
|
||
|
|
h = 110.0
|
||
|
|
|
||
|
|
# enable buttons if we have prices..
|
||
|
|
tickets2_price = ba.internal.get_price('tickets2')
|
||
|
|
tickets3_price = ba.internal.get_price('tickets3')
|
||
|
|
tickets4_price = ba.internal.get_price('tickets4')
|
||
|
|
tickets5_price = ba.internal.get_price('tickets5')
|
||
|
|
|
||
|
|
# TEMP
|
||
|
|
# tickets1_price = '$0.99'
|
||
|
|
# tickets2_price = '$4.99'
|
||
|
|
# tickets3_price = '$9.99'
|
||
|
|
# tickets4_price = '$19.99'
|
||
|
|
# tickets5_price = '$49.99'
|
||
|
|
|
||
|
|
_add_button(
|
||
|
|
'tickets2',
|
||
|
|
enabled=(tickets2_price is not None),
|
||
|
|
position=(
|
||
|
|
self._width * 0.5 - spacing * 1.5 - b_size[0] * 2.0 + h,
|
||
|
|
v,
|
||
|
|
),
|
||
|
|
size=b_size,
|
||
|
|
label=c2txt,
|
||
|
|
price=tickets2_price,
|
||
|
|
tex_name='ticketsMore',
|
||
|
|
) # 0.99-ish
|
||
|
|
_add_button(
|
||
|
|
'tickets3',
|
||
|
|
enabled=(tickets3_price is not None),
|
||
|
|
position=(
|
||
|
|
self._width * 0.5 - spacing * 0.5 - b_size[0] * 1.0 + h,
|
||
|
|
v,
|
||
|
|
),
|
||
|
|
size=b_size,
|
||
|
|
label=c3txt,
|
||
|
|
price=tickets3_price,
|
||
|
|
tex_name='ticketRoll',
|
||
|
|
) # 4.99-ish
|
||
|
|
v -= b_size[1] - 5
|
||
|
|
_add_button(
|
||
|
|
'tickets4',
|
||
|
|
enabled=(tickets4_price is not None),
|
||
|
|
position=(
|
||
|
|
self._width * 0.5 - spacing * 1.5 - b_size[0] * 2.0 + h,
|
||
|
|
v,
|
||
|
|
),
|
||
|
|
size=b_size,
|
||
|
|
label=c4txt,
|
||
|
|
price=tickets4_price,
|
||
|
|
tex_name='ticketRollBig',
|
||
|
|
tex_scale=1.2,
|
||
|
|
) # 9.99-ish
|
||
|
|
_add_button(
|
||
|
|
'tickets5',
|
||
|
|
enabled=(tickets5_price is not None),
|
||
|
|
position=(
|
||
|
|
self._width * 0.5 - spacing * 0.5 - b_size[0] * 1.0 + h,
|
||
|
|
v,
|
||
|
|
),
|
||
|
|
size=b_size,
|
||
|
|
label=c5txt,
|
||
|
|
price=tickets5_price,
|
||
|
|
tex_name='ticketRolls',
|
||
|
|
tex_scale=1.2,
|
||
|
|
) # 19.99-ish
|
||
|
|
|
||
|
|
self._enable_ad_button = ba.internal.has_video_ads()
|
||
|
|
h = self._width * 0.5 + 110.0
|
||
|
|
v = self._height - b_size[1] - 115.0
|
||
|
|
|
||
|
|
if self._enable_ad_button:
|
||
|
|
h_offs = 35
|
||
|
|
b_size_3 = (150, 120)
|
||
|
|
cdb = _add_button(
|
||
|
|
'ad',
|
||
|
|
position=(h + h_offs, v),
|
||
|
|
size=b_size_3,
|
||
|
|
label=ba.Lstr(
|
||
|
|
resource=self._r + '.ticketsFromASponsorText',
|
||
|
|
subs=[
|
||
|
|
(
|
||
|
|
'${COUNT}',
|
||
|
|
str(
|
||
|
|
ba.internal.get_v1_account_misc_read_val(
|
||
|
|
'sponsorTickets', 5
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
],
|
||
|
|
),
|
||
|
|
tex_name='ticketsMore',
|
||
|
|
enabled=self._enable_ad_button,
|
||
|
|
tex_opacity=0.6,
|
||
|
|
tex_scale=0.7,
|
||
|
|
text_scale=0.7,
|
||
|
|
)
|
||
|
|
ba.buttonwidget(
|
||
|
|
edit=cdb,
|
||
|
|
color=(0.65, 0.5, 0.7)
|
||
|
|
if self._enable_ad_button
|
||
|
|
else (0.5, 0.5, 0.5),
|
||
|
|
)
|
||
|
|
|
||
|
|
self._ad_free_text = ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
text=ba.Lstr(resource=self._r + '.freeText'),
|
||
|
|
position=(
|
||
|
|
h + h_offs + b_size_3[0] * 0.5,
|
||
|
|
v + b_size_3[1] * 0.5 + 25,
|
||
|
|
),
|
||
|
|
size=(0, 0),
|
||
|
|
color=(1, 1, 0, 1.0)
|
||
|
|
if self._enable_ad_button
|
||
|
|
else (1, 1, 1, 0.2),
|
||
|
|
draw_controller=cdb,
|
||
|
|
rotate=15,
|
||
|
|
shadow=1.0,
|
||
|
|
maxwidth=150,
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
scale=1.0,
|
||
|
|
)
|
||
|
|
v -= 125
|
||
|
|
else:
|
||
|
|
v -= 20
|
||
|
|
|
||
|
|
if True: # pylint: disable=using-constant-test
|
||
|
|
h_offs = 35
|
||
|
|
b_size_3 = (150, 120)
|
||
|
|
cdb = _add_button(
|
||
|
|
'app_invite',
|
||
|
|
position=(h + h_offs, v),
|
||
|
|
size=b_size_3,
|
||
|
|
label=ba.Lstr(
|
||
|
|
resource='gatherWindow.earnTicketsForRecommendingText',
|
||
|
|
subs=[
|
||
|
|
(
|
||
|
|
'${COUNT}',
|
||
|
|
str(
|
||
|
|
ba.internal.get_v1_account_misc_read_val(
|
||
|
|
'sponsorTickets', 5
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
],
|
||
|
|
),
|
||
|
|
tex_name='ticketsMore',
|
||
|
|
enabled=True,
|
||
|
|
tex_opacity=0.6,
|
||
|
|
tex_scale=0.7,
|
||
|
|
text_scale=0.7,
|
||
|
|
)
|
||
|
|
ba.buttonwidget(edit=cdb, color=(0.65, 0.5, 0.7))
|
||
|
|
|
||
|
|
ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
text=ba.Lstr(resource=self._r + '.freeText'),
|
||
|
|
position=(
|
||
|
|
h + h_offs + b_size_3[0] * 0.5,
|
||
|
|
v + b_size_3[1] * 0.5 + 25,
|
||
|
|
),
|
||
|
|
size=(0, 0),
|
||
|
|
color=(1, 1, 0, 1.0),
|
||
|
|
draw_controller=cdb,
|
||
|
|
rotate=15,
|
||
|
|
shadow=1.0,
|
||
|
|
maxwidth=150,
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
scale=1.0,
|
||
|
|
)
|
||
|
|
tc_y_offs = 0
|
||
|
|
|
||
|
|
h = self._width - (185 + x_inset)
|
||
|
|
v = self._height - 95 + tc_y_offs
|
||
|
|
|
||
|
|
txt1 = (
|
||
|
|
ba.Lstr(resource=self._r + '.youHaveText')
|
||
|
|
.evaluate()
|
||
|
|
.partition('${COUNT}')[0]
|
||
|
|
.strip()
|
||
|
|
)
|
||
|
|
txt2 = (
|
||
|
|
ba.Lstr(resource=self._r + '.youHaveText')
|
||
|
|
.evaluate()
|
||
|
|
.rpartition('${COUNT}')[-1]
|
||
|
|
.strip()
|
||
|
|
)
|
||
|
|
|
||
|
|
ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
text=txt1,
|
||
|
|
position=(h, v),
|
||
|
|
size=(0, 0),
|
||
|
|
color=(0.5, 0.5, 0.6),
|
||
|
|
maxwidth=200,
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
scale=0.8,
|
||
|
|
)
|
||
|
|
v -= 30
|
||
|
|
self._ticket_count_text = ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
position=(h, v),
|
||
|
|
size=(0, 0),
|
||
|
|
color=(0.2, 1.0, 0.2),
|
||
|
|
maxwidth=200,
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
scale=1.6,
|
||
|
|
)
|
||
|
|
v -= 30
|
||
|
|
ba.textwidget(
|
||
|
|
parent=self._root_widget,
|
||
|
|
text=txt2,
|
||
|
|
position=(h, v),
|
||
|
|
size=(0, 0),
|
||
|
|
color=(0.5, 0.5, 0.6),
|
||
|
|
maxwidth=200,
|
||
|
|
h_align='center',
|
||
|
|
v_align='center',
|
||
|
|
scale=0.8,
|
||
|
|
)
|
||
|
|
|
||
|
|
# update count now and once per second going forward..
|
||
|
|
self._ticking_node: ba.Node | None = None
|
||
|
|
self._smooth_ticket_count: float | None = None
|
||
|
|
self._ticket_count = 0
|
||
|
|
self._update()
|
||
|
|
self._update_timer = ba.Timer(
|
||
|
|
1.0,
|
||
|
|
ba.WeakCall(self._update),
|
||
|
|
timetype=ba.TimeType.REAL,
|
||
|
|
repeat=True,
|
||
|
|
)
|
||
|
|
self._smooth_increase_speed = 1.0
|
||
|
|
|
||
|
|
def __del__(self) -> None:
|
||
|
|
if self._ticking_node is not None:
|
||
|
|
self._ticking_node.delete()
|
||
|
|
self._ticking_node = None
|
||
|
|
|
||
|
|
def _smooth_update(self) -> None:
|
||
|
|
if not self._ticket_count_text:
|
||
|
|
self._smooth_update_timer = None
|
||
|
|
return
|
||
|
|
|
||
|
|
finished = False
|
||
|
|
|
||
|
|
# if we're going down, do it immediately
|
||
|
|
assert self._smooth_ticket_count is not None
|
||
|
|
if int(self._smooth_ticket_count) >= self._ticket_count:
|
||
|
|
self._smooth_ticket_count = float(self._ticket_count)
|
||
|
|
finished = True
|
||
|
|
else:
|
||
|
|
# we're going up; start a sound if need be
|
||
|
|
self._smooth_ticket_count = min(
|
||
|
|
self._smooth_ticket_count + 1.0 * self._smooth_increase_speed,
|
||
|
|
self._ticket_count,
|
||
|
|
)
|
||
|
|
if int(self._smooth_ticket_count) >= self._ticket_count:
|
||
|
|
finished = True
|
||
|
|
self._smooth_ticket_count = float(self._ticket_count)
|
||
|
|
elif self._ticking_node is None:
|
||
|
|
with ba.Context('ui'):
|
||
|
|
self._ticking_node = ba.newnode(
|
||
|
|
'sound',
|
||
|
|
attrs={
|
||
|
|
'sound': ba.getsound('scoreIncrease'),
|
||
|
|
'positional': False,
|
||
|
|
},
|
||
|
|
)
|
||
|
|
|
||
|
|
ba.textwidget(
|
||
|
|
edit=self._ticket_count_text,
|
||
|
|
text=str(int(self._smooth_ticket_count)),
|
||
|
|
)
|
||
|
|
|
||
|
|
# if we've reached the target, kill the timer/sound/etc
|
||
|
|
if finished:
|
||
|
|
self._smooth_update_timer = None
|
||
|
|
if self._ticking_node is not None:
|
||
|
|
self._ticking_node.delete()
|
||
|
|
self._ticking_node = None
|
||
|
|
ba.playsound(ba.getsound('cashRegister2'))
|
||
|
|
|
||
|
|
def _update(self) -> None:
|
||
|
|
import datetime
|
||
|
|
|
||
|
|
# if we somehow get signed out, just die..
|
||
|
|
if ba.internal.get_v1_account_state() != 'signed_in':
|
||
|
|
self._back()
|
||
|
|
return
|
||
|
|
|
||
|
|
self._ticket_count = ba.internal.get_v1_account_ticket_count()
|
||
|
|
|
||
|
|
# update our incentivized ad button depending on whether ads are
|
||
|
|
# available
|
||
|
|
if self._ad_button is not None:
|
||
|
|
next_reward_ad_time = ba.internal.get_v1_account_misc_read_val_2(
|
||
|
|
'nextRewardAdTime', None
|
||
|
|
)
|
||
|
|
if next_reward_ad_time is not None:
|
||
|
|
next_reward_ad_time = datetime.datetime.utcfromtimestamp(
|
||
|
|
next_reward_ad_time
|
||
|
|
)
|
||
|
|
now = datetime.datetime.utcnow()
|
||
|
|
if ba.internal.have_incentivized_ad() and (
|
||
|
|
next_reward_ad_time is None or next_reward_ad_time <= now
|
||
|
|
):
|
||
|
|
self._ad_button_greyed = False
|
||
|
|
ba.buttonwidget(edit=self._ad_button, color=(0.65, 0.5, 0.7))
|
||
|
|
ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 1.0))
|
||
|
|
ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 1))
|
||
|
|
ba.imagewidget(edit=self._ad_image, opacity=0.6)
|
||
|
|
ba.textwidget(edit=self._ad_time_text, text='')
|
||
|
|
else:
|
||
|
|
self._ad_button_greyed = True
|
||
|
|
ba.buttonwidget(edit=self._ad_button, color=(0.5, 0.5, 0.5))
|
||
|
|
ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 0.2))
|
||
|
|
ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 0.2))
|
||
|
|
ba.imagewidget(edit=self._ad_image, opacity=0.6 * 0.25)
|
||
|
|
sval: str | ba.Lstr
|
||
|
|
if (
|
||
|
|
next_reward_ad_time is not None
|
||
|
|
and next_reward_ad_time > now
|
||
|
|
):
|
||
|
|
sval = ba.timestring(
|
||
|
|
(next_reward_ad_time - now).total_seconds() * 1000.0,
|
||
|
|
centi=False,
|
||
|
|
timeformat=ba.TimeFormat.MILLISECONDS,
|
||
|
|
)
|
||
|
|
else:
|
||
|
|
sval = ''
|
||
|
|
ba.textwidget(edit=self._ad_time_text, text=sval)
|
||
|
|
|
||
|
|
# if this is our first update, assign immediately; otherwise kick
|
||
|
|
# off a smooth transition if the value has changed
|
||
|
|
if self._smooth_ticket_count is None:
|
||
|
|
self._smooth_ticket_count = float(self._ticket_count)
|
||
|
|
self._smooth_update() # will set the text widget
|
||
|
|
|
||
|
|
elif (
|
||
|
|
self._ticket_count != int(self._smooth_ticket_count)
|
||
|
|
and self._smooth_update_timer is None
|
||
|
|
):
|
||
|
|
self._smooth_update_timer = ba.Timer(
|
||
|
|
0.05,
|
||
|
|
ba.WeakCall(self._smooth_update),
|
||
|
|
repeat=True,
|
||
|
|
timetype=ba.TimeType.REAL,
|
||
|
|
)
|
||
|
|
diff = abs(float(self._ticket_count) - self._smooth_ticket_count)
|
||
|
|
self._smooth_increase_speed = (
|
||
|
|
diff / 100.0
|
||
|
|
if diff >= 5000
|
||
|
|
else diff / 50.0
|
||
|
|
if diff >= 1500
|
||
|
|
else diff / 30.0
|
||
|
|
if diff >= 500
|
||
|
|
else diff / 15.0
|
||
|
|
)
|
||
|
|
|
||
|
|
def _disabled_press(self) -> None:
|
||
|
|
|
||
|
|
# if we're on a platform without purchases, inform the user they
|
||
|
|
# can link their accounts and buy stuff elsewhere
|
||
|
|
app = ba.app
|
||
|
|
if (
|
||
|
|
app.test_build
|
||
|
|
or (
|
||
|
|
app.platform == 'android'
|
||
|
|
and app.subplatform in ['oculus', 'cardboard']
|
||
|
|
)
|
||
|
|
) and ba.internal.get_v1_account_misc_read_val(
|
||
|
|
'allowAccountLinking2', False
|
||
|
|
):
|
||
|
|
ba.screenmessage(
|
||
|
|
ba.Lstr(resource=self._r + '.unavailableLinkAccountText'),
|
||
|
|
color=(1, 0.5, 0),
|
||
|
|
)
|
||
|
|
else:
|
||
|
|
ba.screenmessage(
|
||
|
|
ba.Lstr(resource=self._r + '.unavailableText'),
|
||
|
|
color=(1, 0.5, 0),
|
||
|
|
)
|
||
|
|
ba.playsound(ba.getsound('error'))
|
||
|
|
|
||
|
|
def _purchase(self, item: str) -> None:
|
||
|
|
from bastd.ui import account
|
||
|
|
from bastd.ui import appinvite
|
||
|
|
from ba.internal import master_server_get
|
||
|
|
|
||
|
|
if item == 'app_invite':
|
||
|
|
if ba.internal.get_v1_account_state() != 'signed_in':
|
||
|
|
account.show_sign_in_prompt()
|
||
|
|
return
|
||
|
|
appinvite.handle_app_invites_press()
|
||
|
|
return
|
||
|
|
# here we ping the server to ask if it's valid for us to
|
||
|
|
# purchase this.. (better to fail now than after we've paid locally)
|
||
|
|
app = ba.app
|
||
|
|
master_server_get(
|
||
|
|
'bsAccountPurchaseCheck',
|
||
|
|
{
|
||
|
|
'item': item,
|
||
|
|
'platform': app.platform,
|
||
|
|
'subplatform': app.subplatform,
|
||
|
|
'version': app.version,
|
||
|
|
'buildNumber': app.build_number,
|
||
|
|
},
|
||
|
|
callback=ba.WeakCall(self._purchase_check_result, item),
|
||
|
|
)
|
||
|
|
|
||
|
|
def _purchase_check_result(
|
||
|
|
self, item: str, result: dict[str, Any] | None
|
||
|
|
) -> None:
|
||
|
|
if result is None:
|
||
|
|
ba.playsound(ba.getsound('error'))
|
||
|
|
ba.screenmessage(
|
||
|
|
ba.Lstr(resource='internal.unavailableNoConnectionText'),
|
||
|
|
color=(1, 0, 0),
|
||
|
|
)
|
||
|
|
else:
|
||
|
|
if result['allow']:
|
||
|
|
self._do_purchase(item)
|
||
|
|
else:
|
||
|
|
if result['reason'] == 'versionTooOld':
|
||
|
|
ba.playsound(ba.getsound('error'))
|
||
|
|
ba.screenmessage(
|
||
|
|
ba.Lstr(resource='getTicketsWindow.versionTooOldText'),
|
||
|
|
color=(1, 0, 0),
|
||
|
|
)
|
||
|
|
else:
|
||
|
|
ba.playsound(ba.getsound('error'))
|
||
|
|
ba.screenmessage(
|
||
|
|
ba.Lstr(resource='getTicketsWindow.unavailableText'),
|
||
|
|
color=(1, 0, 0),
|
||
|
|
)
|
||
|
|
|
||
|
|
# actually start the purchase locally..
|
||
|
|
def _do_purchase(self, item: str) -> None:
|
||
|
|
if item == 'ad':
|
||
|
|
import datetime
|
||
|
|
|
||
|
|
# if ads are disabled until some time, error..
|
||
|
|
next_reward_ad_time = ba.internal.get_v1_account_misc_read_val_2(
|
||
|
|
'nextRewardAdTime', None
|
||
|
|
)
|
||
|
|
if next_reward_ad_time is not None:
|
||
|
|
next_reward_ad_time = datetime.datetime.utcfromtimestamp(
|
||
|
|
next_reward_ad_time
|
||
|
|
)
|
||
|
|
now = datetime.datetime.utcnow()
|
||
|
|
if (
|
||
|
|
next_reward_ad_time is not None and next_reward_ad_time > now
|
||
|
|
) or self._ad_button_greyed:
|
||
|
|
ba.playsound(ba.getsound('error'))
|
||
|
|
ba.screenmessage(
|
||
|
|
ba.Lstr(
|
||
|
|
resource='getTicketsWindow.unavailableTemporarilyText'
|
||
|
|
),
|
||
|
|
color=(1, 0, 0),
|
||
|
|
)
|
||
|
|
elif self._enable_ad_button:
|
||
|
|
ba.app.ads.show_ad('tickets')
|
||
|
|
else:
|
||
|
|
ba.internal.purchase(item)
|
||
|
|
|
||
|
|
def _back(self) -> None:
|
||
|
|
from bastd.ui.store import browser
|
||
|
|
|
||
|
|
if self._transitioning_out:
|
||
|
|
return
|
||
|
|
ba.containerwidget(
|
||
|
|
edit=self._root_widget, transition=self._transition_out
|
||
|
|
)
|
||
|
|
if not self._modal:
|
||
|
|
window = browser.StoreBrowserWindow(
|
||
|
|
transition='in_left',
|
||
|
|
modal=self._from_modal_store,
|
||
|
|
back_location=self._store_back_location,
|
||
|
|
).get_root_widget()
|
||
|
|
if not self._from_modal_store:
|
||
|
|
ba.app.ui.set_main_menu_window(window)
|
||
|
|
self._transitioning_out = True
|
||
|
|
|
||
|
|
|
||
|
|
def show_get_tickets_prompt() -> None:
|
||
|
|
"""Show a 'not enough tickets' prompt with an option to purchase more.
|
||
|
|
|
||
|
|
Note that the purchase option may not always be available
|
||
|
|
depending on the build of the game.
|
||
|
|
"""
|
||
|
|
from bastd.ui.confirm import ConfirmWindow
|
||
|
|
|
||
|
|
if ba.app.allow_ticket_purchases:
|
||
|
|
ConfirmWindow(
|
||
|
|
ba.Lstr(
|
||
|
|
translate=(
|
||
|
|
'serverResponses',
|
||
|
|
'You don\'t have enough tickets for this!',
|
||
|
|
)
|
||
|
|
),
|
||
|
|
lambda: GetCurrencyWindow(modal=True),
|
||
|
|
ok_text=ba.Lstr(resource='getTicketsWindow.titleText'),
|
||
|
|
width=460,
|
||
|
|
height=130,
|
||
|
|
)
|
||
|
|
else:
|
||
|
|
ConfirmWindow(
|
||
|
|
ba.Lstr(
|
||
|
|
translate=(
|
||
|
|
'serverResponses',
|
||
|
|
'You don\'t have enough tickets for this!',
|
||
|
|
)
|
||
|
|
),
|
||
|
|
cancel_button=False,
|
||
|
|
width=460,
|
||
|
|
height=130,
|
||
|
|
)
|