bastd.ui.getcurrency
UI functionality for purchasing/acquiring currency.
1# Released under the MIT License. See LICENSE for details. 2# 3"""UI functionality for purchasing/acquiring currency.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING 8 9import _ba 10import ba 11 12if TYPE_CHECKING: 13 from typing import Any 14 15 16class GetCurrencyWindow(ba.Window): 17 """Window for purchasing/acquiring currency.""" 18 19 def __init__(self, 20 transition: str = 'in_right', 21 from_modal_store: bool = False, 22 modal: bool = False, 23 origin_widget: ba.Widget | None = None, 24 store_back_location: str | None = None): 25 # pylint: disable=too-many-statements 26 # pylint: disable=too-many-locals 27 28 ba.set_analytics_screen('Get Tickets Window') 29 30 self._transitioning_out = False 31 self._store_back_location = store_back_location # ew. 32 33 self._ad_button_greyed = False 34 self._smooth_update_timer: ba.Timer | None = None 35 self._ad_button = None 36 self._ad_label = None 37 self._ad_image = None 38 self._ad_time_text = None 39 40 # If they provided an origin-widget, scale up from that. 41 scale_origin: tuple[float, float] | None 42 if origin_widget is not None: 43 self._transition_out = 'out_scale' 44 scale_origin = origin_widget.get_screen_space_center() 45 transition = 'in_scale' 46 else: 47 self._transition_out = 'out_right' 48 scale_origin = None 49 50 uiscale = ba.app.ui.uiscale 51 self._width = 1000.0 if uiscale is ba.UIScale.SMALL else 800.0 52 x_inset = 100.0 if uiscale is ba.UIScale.SMALL else 0.0 53 self._height = 480.0 54 55 self._modal = modal 56 self._from_modal_store = from_modal_store 57 self._r = 'getTicketsWindow' 58 59 top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 60 61 super().__init__(root_widget=ba.containerwidget( 62 size=(self._width, self._height + top_extra), 63 transition=transition, 64 scale_origin_stack_offset=scale_origin, 65 color=(0.4, 0.37, 0.55), 66 scale=(1.63 if uiscale is ba.UIScale.SMALL else 67 1.2 if uiscale is ba.UIScale.MEDIUM else 1.0), 68 stack_offset=(0, -3) if uiscale is ba.UIScale.SMALL else (0, 0))) 69 70 btn = ba.buttonwidget( 71 parent=self._root_widget, 72 position=(55 + x_inset, self._height - 79), 73 size=(140, 60), 74 scale=1.0, 75 autoselect=True, 76 label=ba.Lstr(resource='doneText' if modal else 'backText'), 77 button_type='regular' if modal else 'back', 78 on_activate_call=self._back) 79 80 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 81 82 ba.textwidget(parent=self._root_widget, 83 position=(self._width * 0.5, self._height - 55), 84 size=(0, 0), 85 color=ba.app.ui.title_color, 86 scale=1.2, 87 h_align='center', 88 v_align='center', 89 text=ba.Lstr(resource=self._r + '.titleText'), 90 maxwidth=290) 91 92 if not modal: 93 ba.buttonwidget(edit=btn, 94 button_type='backSmall', 95 size=(60, 60), 96 label=ba.charstr(ba.SpecialChar.BACK)) 97 98 b_size = (220.0, 180.0) 99 v = self._height - b_size[1] - 80 100 spacing = 1 101 102 self._ad_button = None 103 104 def _add_button(item: str, 105 position: tuple[float, float], 106 size: tuple[float, float], 107 label: ba.Lstr, 108 price: str | None = None, 109 tex_name: str | None = None, 110 tex_opacity: float = 1.0, 111 tex_scale: float = 1.0, 112 enabled: bool = True, 113 text_scale: float = 1.0) -> ba.Widget: 114 btn2 = ba.buttonwidget( 115 parent=self._root_widget, 116 position=position, 117 button_type='square', 118 size=size, 119 label='', 120 autoselect=True, 121 color=None if enabled else (0.5, 0.5, 0.5), 122 on_activate_call=(ba.Call(self._purchase, item) 123 if enabled else self._disabled_press)) 124 txt = ba.textwidget(parent=self._root_widget, 125 text=label, 126 position=(position[0] + size[0] * 0.5, 127 position[1] + size[1] * 0.3), 128 scale=text_scale, 129 maxwidth=size[0] * 0.75, 130 size=(0, 0), 131 h_align='center', 132 v_align='center', 133 draw_controller=btn2, 134 color=(0.7, 0.9, 0.7, 1.0 if enabled else 0.2)) 135 if price is not None and enabled: 136 ba.textwidget(parent=self._root_widget, 137 text=price, 138 position=(position[0] + size[0] * 0.5, 139 position[1] + size[1] * 0.17), 140 scale=0.7, 141 maxwidth=size[0] * 0.75, 142 size=(0, 0), 143 h_align='center', 144 v_align='center', 145 draw_controller=btn2, 146 color=(0.4, 0.9, 0.4, 1.0)) 147 i = None 148 if tex_name is not None: 149 tex_size = 90.0 * tex_scale 150 i = ba.imagewidget( 151 parent=self._root_widget, 152 texture=ba.gettexture(tex_name), 153 position=(position[0] + size[0] * 0.5 - tex_size * 0.5, 154 position[1] + size[1] * 0.66 - tex_size * 0.5), 155 size=(tex_size, tex_size), 156 draw_controller=btn2, 157 opacity=tex_opacity * (1.0 if enabled else 0.25)) 158 if item == 'ad': 159 self._ad_button = btn2 160 self._ad_label = txt 161 assert i is not None 162 self._ad_image = i 163 self._ad_time_text = ba.textwidget( 164 parent=self._root_widget, 165 text='1m 10s', 166 position=(position[0] + size[0] * 0.5, 167 position[1] + size[1] * 0.5), 168 scale=text_scale * 1.2, 169 maxwidth=size[0] * 0.85, 170 size=(0, 0), 171 h_align='center', 172 v_align='center', 173 draw_controller=btn2, 174 color=(0.4, 0.9, 0.4, 1.0)) 175 return btn2 176 177 rsrc = self._r + '.ticketsText' 178 179 c2txt = ba.Lstr( 180 resource=rsrc, 181 subs=[('${COUNT}', 182 str(_ba.get_v1_account_misc_read_val('tickets2Amount', 183 500)))]) 184 c3txt = ba.Lstr( 185 resource=rsrc, 186 subs=[ 187 ('${COUNT}', 188 str(_ba.get_v1_account_misc_read_val('tickets3Amount', 1500))) 189 ]) 190 c4txt = ba.Lstr( 191 resource=rsrc, 192 subs=[ 193 ('${COUNT}', 194 str(_ba.get_v1_account_misc_read_val('tickets4Amount', 5000))) 195 ]) 196 c5txt = ba.Lstr( 197 resource=rsrc, 198 subs=[ 199 ('${COUNT}', 200 str(_ba.get_v1_account_misc_read_val('tickets5Amount', 201 15000))) 202 ]) 203 204 h = 110.0 205 206 # enable buttons if we have prices.. 207 tickets2_price = _ba.get_price('tickets2') 208 tickets3_price = _ba.get_price('tickets3') 209 tickets4_price = _ba.get_price('tickets4') 210 tickets5_price = _ba.get_price('tickets5') 211 212 # TEMP 213 # tickets1_price = '$0.99' 214 # tickets2_price = '$4.99' 215 # tickets3_price = '$9.99' 216 # tickets4_price = '$19.99' 217 # tickets5_price = '$49.99' 218 219 _add_button('tickets2', 220 enabled=(tickets2_price is not None), 221 position=(self._width * 0.5 - spacing * 1.5 - 222 b_size[0] * 2.0 + h, v), 223 size=b_size, 224 label=c2txt, 225 price=tickets2_price, 226 tex_name='ticketsMore') # 0.99-ish 227 _add_button('tickets3', 228 enabled=(tickets3_price is not None), 229 position=(self._width * 0.5 - spacing * 0.5 - 230 b_size[0] * 1.0 + h, v), 231 size=b_size, 232 label=c3txt, 233 price=tickets3_price, 234 tex_name='ticketRoll') # 4.99-ish 235 v -= b_size[1] - 5 236 _add_button('tickets4', 237 enabled=(tickets4_price is not None), 238 position=(self._width * 0.5 - spacing * 1.5 - 239 b_size[0] * 2.0 + h, v), 240 size=b_size, 241 label=c4txt, 242 price=tickets4_price, 243 tex_name='ticketRollBig', 244 tex_scale=1.2) # 9.99-ish 245 _add_button('tickets5', 246 enabled=(tickets5_price is not None), 247 position=(self._width * 0.5 - spacing * 0.5 - 248 b_size[0] * 1.0 + h, v), 249 size=b_size, 250 label=c5txt, 251 price=tickets5_price, 252 tex_name='ticketRolls', 253 tex_scale=1.2) # 19.99-ish 254 255 self._enable_ad_button = _ba.has_video_ads() 256 h = self._width * 0.5 + 110.0 257 v = self._height - b_size[1] - 115.0 258 259 if self._enable_ad_button: 260 h_offs = 35 261 b_size_3 = (150, 120) 262 cdb = _add_button( 263 'ad', 264 position=(h + h_offs, v), 265 size=b_size_3, 266 label=ba.Lstr(resource=self._r + '.ticketsFromASponsorText', 267 subs=[('${COUNT}', 268 str( 269 _ba.get_v1_account_misc_read_val( 270 'sponsorTickets', 5)))]), 271 tex_name='ticketsMore', 272 enabled=self._enable_ad_button, 273 tex_opacity=0.6, 274 tex_scale=0.7, 275 text_scale=0.7) 276 ba.buttonwidget(edit=cdb, 277 color=(0.65, 0.5, 278 0.7) if self._enable_ad_button else 279 (0.5, 0.5, 0.5)) 280 281 self._ad_free_text = ba.textwidget( 282 parent=self._root_widget, 283 text=ba.Lstr(resource=self._r + '.freeText'), 284 position=(h + h_offs + b_size_3[0] * 0.5, 285 v + b_size_3[1] * 0.5 + 25), 286 size=(0, 0), 287 color=(1, 1, 0, 1.0) if self._enable_ad_button else 288 (1, 1, 1, 0.2), 289 draw_controller=cdb, 290 rotate=15, 291 shadow=1.0, 292 maxwidth=150, 293 h_align='center', 294 v_align='center', 295 scale=1.0) 296 v -= 125 297 else: 298 v -= 20 299 300 if True: # pylint: disable=using-constant-test 301 h_offs = 35 302 b_size_3 = (150, 120) 303 cdb = _add_button( 304 'app_invite', 305 position=(h + h_offs, v), 306 size=b_size_3, 307 label=ba.Lstr( 308 resource='gatherWindow.earnTicketsForRecommendingText', 309 subs=[('${COUNT}', 310 str( 311 _ba.get_v1_account_misc_read_val( 312 'sponsorTickets', 5)))]), 313 tex_name='ticketsMore', 314 enabled=True, 315 tex_opacity=0.6, 316 tex_scale=0.7, 317 text_scale=0.7) 318 ba.buttonwidget(edit=cdb, color=(0.65, 0.5, 0.7)) 319 320 ba.textwidget(parent=self._root_widget, 321 text=ba.Lstr(resource=self._r + '.freeText'), 322 position=(h + h_offs + b_size_3[0] * 0.5, 323 v + b_size_3[1] * 0.5 + 25), 324 size=(0, 0), 325 color=(1, 1, 0, 1.0), 326 draw_controller=cdb, 327 rotate=15, 328 shadow=1.0, 329 maxwidth=150, 330 h_align='center', 331 v_align='center', 332 scale=1.0) 333 tc_y_offs = 0 334 335 h = self._width - (185 + x_inset) 336 v = self._height - 95 + tc_y_offs 337 338 txt1 = (ba.Lstr( 339 resource=self._r + 340 '.youHaveText').evaluate().partition('${COUNT}')[0].strip()) 341 txt2 = (ba.Lstr( 342 resource=self._r + 343 '.youHaveText').evaluate().rpartition('${COUNT}')[-1].strip()) 344 345 ba.textwidget(parent=self._root_widget, 346 text=txt1, 347 position=(h, v), 348 size=(0, 0), 349 color=(0.5, 0.5, 0.6), 350 maxwidth=200, 351 h_align='center', 352 v_align='center', 353 scale=0.8) 354 v -= 30 355 self._ticket_count_text = ba.textwidget(parent=self._root_widget, 356 position=(h, v), 357 size=(0, 0), 358 color=(0.2, 1.0, 0.2), 359 maxwidth=200, 360 h_align='center', 361 v_align='center', 362 scale=1.6) 363 v -= 30 364 ba.textwidget(parent=self._root_widget, 365 text=txt2, 366 position=(h, v), 367 size=(0, 0), 368 color=(0.5, 0.5, 0.6), 369 maxwidth=200, 370 h_align='center', 371 v_align='center', 372 scale=0.8) 373 374 # update count now and once per second going forward.. 375 self._ticking_node: ba.Node | None = None 376 self._smooth_ticket_count: float | None = None 377 self._ticket_count = 0 378 self._update() 379 self._update_timer = ba.Timer(1.0, 380 ba.WeakCall(self._update), 381 timetype=ba.TimeType.REAL, 382 repeat=True) 383 self._smooth_increase_speed = 1.0 384 385 def __del__(self) -> None: 386 if self._ticking_node is not None: 387 self._ticking_node.delete() 388 self._ticking_node = None 389 390 def _smooth_update(self) -> None: 391 if not self._ticket_count_text: 392 self._smooth_update_timer = None 393 return 394 395 finished = False 396 397 # if we're going down, do it immediately 398 assert self._smooth_ticket_count is not None 399 if int(self._smooth_ticket_count) >= self._ticket_count: 400 self._smooth_ticket_count = float(self._ticket_count) 401 finished = True 402 else: 403 # we're going up; start a sound if need be 404 self._smooth_ticket_count = min( 405 self._smooth_ticket_count + 1.0 * self._smooth_increase_speed, 406 self._ticket_count) 407 if int(self._smooth_ticket_count) >= self._ticket_count: 408 finished = True 409 self._smooth_ticket_count = float(self._ticket_count) 410 elif self._ticking_node is None: 411 with ba.Context('ui'): 412 self._ticking_node = ba.newnode( 413 'sound', 414 attrs={ 415 'sound': ba.getsound('scoreIncrease'), 416 'positional': False 417 }) 418 419 ba.textwidget(edit=self._ticket_count_text, 420 text=str(int(self._smooth_ticket_count))) 421 422 # if we've reached the target, kill the timer/sound/etc 423 if finished: 424 self._smooth_update_timer = None 425 if self._ticking_node is not None: 426 self._ticking_node.delete() 427 self._ticking_node = None 428 ba.playsound(ba.getsound('cashRegister2')) 429 430 def _update(self) -> None: 431 import datetime 432 433 # if we somehow get signed out, just die.. 434 if _ba.get_v1_account_state() != 'signed_in': 435 self._back() 436 return 437 438 self._ticket_count = _ba.get_v1_account_ticket_count() 439 440 # update our incentivized ad button depending on whether ads are 441 # available 442 if self._ad_button is not None: 443 next_reward_ad_time = _ba.get_v1_account_misc_read_val_2( 444 'nextRewardAdTime', None) 445 if next_reward_ad_time is not None: 446 next_reward_ad_time = datetime.datetime.utcfromtimestamp( 447 next_reward_ad_time) 448 now = datetime.datetime.utcnow() 449 if (_ba.have_incentivized_ad() and 450 (next_reward_ad_time is None or next_reward_ad_time <= now)): 451 self._ad_button_greyed = False 452 ba.buttonwidget(edit=self._ad_button, color=(0.65, 0.5, 0.7)) 453 ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 1.0)) 454 ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 1)) 455 ba.imagewidget(edit=self._ad_image, opacity=0.6) 456 ba.textwidget(edit=self._ad_time_text, text='') 457 else: 458 self._ad_button_greyed = True 459 ba.buttonwidget(edit=self._ad_button, color=(0.5, 0.5, 0.5)) 460 ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 0.2)) 461 ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 0.2)) 462 ba.imagewidget(edit=self._ad_image, opacity=0.6 * 0.25) 463 sval: str | ba.Lstr 464 if (next_reward_ad_time is not None 465 and next_reward_ad_time > now): 466 sval = ba.timestring( 467 (next_reward_ad_time - now).total_seconds() * 1000.0, 468 centi=False, 469 timeformat=ba.TimeFormat.MILLISECONDS) 470 else: 471 sval = '' 472 ba.textwidget(edit=self._ad_time_text, text=sval) 473 474 # if this is our first update, assign immediately; otherwise kick 475 # off a smooth transition if the value has changed 476 if self._smooth_ticket_count is None: 477 self._smooth_ticket_count = float(self._ticket_count) 478 self._smooth_update() # will set the text widget 479 480 elif (self._ticket_count != int(self._smooth_ticket_count) 481 and self._smooth_update_timer is None): 482 self._smooth_update_timer = ba.Timer(0.05, 483 ba.WeakCall( 484 self._smooth_update), 485 repeat=True, 486 timetype=ba.TimeType.REAL) 487 diff = abs(float(self._ticket_count) - self._smooth_ticket_count) 488 self._smooth_increase_speed = (diff / 489 100.0 if diff >= 5000 else diff / 490 50.0 if diff >= 1500 else diff / 491 30.0 if diff >= 500 else diff / 492 15.0) 493 494 def _disabled_press(self) -> None: 495 496 # if we're on a platform without purchases, inform the user they 497 # can link their accounts and buy stuff elsewhere 498 app = ba.app 499 if ((app.test_build or 500 (app.platform == 'android' 501 and app.subplatform in ['oculus', 'cardboard'])) 502 and _ba.get_v1_account_misc_read_val('allowAccountLinking2', 503 False)): 504 ba.screenmessage(ba.Lstr(resource=self._r + 505 '.unavailableLinkAccountText'), 506 color=(1, 0.5, 0)) 507 else: 508 ba.screenmessage(ba.Lstr(resource=self._r + '.unavailableText'), 509 color=(1, 0.5, 0)) 510 ba.playsound(ba.getsound('error')) 511 512 def _purchase(self, item: str) -> None: 513 from bastd.ui import account 514 from bastd.ui import appinvite 515 from ba.internal import master_server_get 516 if item == 'app_invite': 517 if _ba.get_v1_account_state() != 'signed_in': 518 account.show_sign_in_prompt() 519 return 520 appinvite.handle_app_invites_press() 521 return 522 # here we ping the server to ask if it's valid for us to 523 # purchase this.. (better to fail now than after we've paid locally) 524 app = ba.app 525 master_server_get('bsAccountPurchaseCheck', { 526 'item': item, 527 'platform': app.platform, 528 'subplatform': app.subplatform, 529 'version': app.version, 530 'buildNumber': app.build_number 531 }, 532 callback=ba.WeakCall(self._purchase_check_result, 533 item)) 534 535 def _purchase_check_result(self, item: str, 536 result: dict[str, Any] | None) -> None: 537 if result is None: 538 ba.playsound(ba.getsound('error')) 539 ba.screenmessage( 540 ba.Lstr(resource='internal.unavailableNoConnectionText'), 541 color=(1, 0, 0)) 542 else: 543 if result['allow']: 544 self._do_purchase(item) 545 else: 546 if result['reason'] == 'versionTooOld': 547 ba.playsound(ba.getsound('error')) 548 ba.screenmessage( 549 ba.Lstr(resource='getTicketsWindow.versionTooOldText'), 550 color=(1, 0, 0)) 551 else: 552 ba.playsound(ba.getsound('error')) 553 ba.screenmessage( 554 ba.Lstr(resource='getTicketsWindow.unavailableText'), 555 color=(1, 0, 0)) 556 557 # actually start the purchase locally.. 558 def _do_purchase(self, item: str) -> None: 559 if item == 'ad': 560 import datetime 561 # if ads are disabled until some time, error.. 562 next_reward_ad_time = _ba.get_v1_account_misc_read_val_2( 563 'nextRewardAdTime', None) 564 if next_reward_ad_time is not None: 565 next_reward_ad_time = datetime.datetime.utcfromtimestamp( 566 next_reward_ad_time) 567 now = datetime.datetime.utcnow() 568 if ((next_reward_ad_time is not None and next_reward_ad_time > now) 569 or self._ad_button_greyed): 570 ba.playsound(ba.getsound('error')) 571 ba.screenmessage(ba.Lstr( 572 resource='getTicketsWindow.unavailableTemporarilyText'), 573 color=(1, 0, 0)) 574 elif self._enable_ad_button: 575 _ba.app.ads.show_ad('tickets') 576 else: 577 _ba.purchase(item) 578 579 def _back(self) -> None: 580 from bastd.ui.store import browser 581 if self._transitioning_out: 582 return 583 ba.containerwidget(edit=self._root_widget, 584 transition=self._transition_out) 585 if not self._modal: 586 window = browser.StoreBrowserWindow( 587 transition='in_left', 588 modal=self._from_modal_store, 589 back_location=self._store_back_location).get_root_widget() 590 if not self._from_modal_store: 591 ba.app.ui.set_main_menu_window(window) 592 self._transitioning_out = True 593 594 595def show_get_tickets_prompt() -> None: 596 """Show a 'not enough tickets' prompt with an option to purchase more. 597 598 Note that the purchase option may not always be available 599 depending on the build of the game. 600 """ 601 from bastd.ui.confirm import ConfirmWindow 602 if ba.app.allow_ticket_purchases: 603 ConfirmWindow( 604 ba.Lstr(translate=('serverResponses', 605 'You don\'t have enough tickets for this!')), 606 lambda: GetCurrencyWindow(modal=True), 607 ok_text=ba.Lstr(resource='getTicketsWindow.titleText'), 608 width=460, 609 height=130) 610 else: 611 ConfirmWindow( 612 ba.Lstr(translate=('serverResponses', 613 'You don\'t have enough tickets for this!')), 614 cancel_button=False, 615 width=460, 616 height=130)
class
GetCurrencyWindow(ba.ui.Window):
17class GetCurrencyWindow(ba.Window): 18 """Window for purchasing/acquiring currency.""" 19 20 def __init__(self, 21 transition: str = 'in_right', 22 from_modal_store: bool = False, 23 modal: bool = False, 24 origin_widget: ba.Widget | None = None, 25 store_back_location: str | None = None): 26 # pylint: disable=too-many-statements 27 # pylint: disable=too-many-locals 28 29 ba.set_analytics_screen('Get Tickets Window') 30 31 self._transitioning_out = False 32 self._store_back_location = store_back_location # ew. 33 34 self._ad_button_greyed = False 35 self._smooth_update_timer: ba.Timer | None = None 36 self._ad_button = None 37 self._ad_label = None 38 self._ad_image = None 39 self._ad_time_text = None 40 41 # If they provided an origin-widget, scale up from that. 42 scale_origin: tuple[float, float] | None 43 if origin_widget is not None: 44 self._transition_out = 'out_scale' 45 scale_origin = origin_widget.get_screen_space_center() 46 transition = 'in_scale' 47 else: 48 self._transition_out = 'out_right' 49 scale_origin = None 50 51 uiscale = ba.app.ui.uiscale 52 self._width = 1000.0 if uiscale is ba.UIScale.SMALL else 800.0 53 x_inset = 100.0 if uiscale is ba.UIScale.SMALL else 0.0 54 self._height = 480.0 55 56 self._modal = modal 57 self._from_modal_store = from_modal_store 58 self._r = 'getTicketsWindow' 59 60 top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 61 62 super().__init__(root_widget=ba.containerwidget( 63 size=(self._width, self._height + top_extra), 64 transition=transition, 65 scale_origin_stack_offset=scale_origin, 66 color=(0.4, 0.37, 0.55), 67 scale=(1.63 if uiscale is ba.UIScale.SMALL else 68 1.2 if uiscale is ba.UIScale.MEDIUM else 1.0), 69 stack_offset=(0, -3) if uiscale is ba.UIScale.SMALL else (0, 0))) 70 71 btn = ba.buttonwidget( 72 parent=self._root_widget, 73 position=(55 + x_inset, self._height - 79), 74 size=(140, 60), 75 scale=1.0, 76 autoselect=True, 77 label=ba.Lstr(resource='doneText' if modal else 'backText'), 78 button_type='regular' if modal else 'back', 79 on_activate_call=self._back) 80 81 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 82 83 ba.textwidget(parent=self._root_widget, 84 position=(self._width * 0.5, self._height - 55), 85 size=(0, 0), 86 color=ba.app.ui.title_color, 87 scale=1.2, 88 h_align='center', 89 v_align='center', 90 text=ba.Lstr(resource=self._r + '.titleText'), 91 maxwidth=290) 92 93 if not modal: 94 ba.buttonwidget(edit=btn, 95 button_type='backSmall', 96 size=(60, 60), 97 label=ba.charstr(ba.SpecialChar.BACK)) 98 99 b_size = (220.0, 180.0) 100 v = self._height - b_size[1] - 80 101 spacing = 1 102 103 self._ad_button = None 104 105 def _add_button(item: str, 106 position: tuple[float, float], 107 size: tuple[float, float], 108 label: ba.Lstr, 109 price: str | None = None, 110 tex_name: str | None = None, 111 tex_opacity: float = 1.0, 112 tex_scale: float = 1.0, 113 enabled: bool = True, 114 text_scale: float = 1.0) -> ba.Widget: 115 btn2 = ba.buttonwidget( 116 parent=self._root_widget, 117 position=position, 118 button_type='square', 119 size=size, 120 label='', 121 autoselect=True, 122 color=None if enabled else (0.5, 0.5, 0.5), 123 on_activate_call=(ba.Call(self._purchase, item) 124 if enabled else self._disabled_press)) 125 txt = ba.textwidget(parent=self._root_widget, 126 text=label, 127 position=(position[0] + size[0] * 0.5, 128 position[1] + size[1] * 0.3), 129 scale=text_scale, 130 maxwidth=size[0] * 0.75, 131 size=(0, 0), 132 h_align='center', 133 v_align='center', 134 draw_controller=btn2, 135 color=(0.7, 0.9, 0.7, 1.0 if enabled else 0.2)) 136 if price is not None and enabled: 137 ba.textwidget(parent=self._root_widget, 138 text=price, 139 position=(position[0] + size[0] * 0.5, 140 position[1] + size[1] * 0.17), 141 scale=0.7, 142 maxwidth=size[0] * 0.75, 143 size=(0, 0), 144 h_align='center', 145 v_align='center', 146 draw_controller=btn2, 147 color=(0.4, 0.9, 0.4, 1.0)) 148 i = None 149 if tex_name is not None: 150 tex_size = 90.0 * tex_scale 151 i = ba.imagewidget( 152 parent=self._root_widget, 153 texture=ba.gettexture(tex_name), 154 position=(position[0] + size[0] * 0.5 - tex_size * 0.5, 155 position[1] + size[1] * 0.66 - tex_size * 0.5), 156 size=(tex_size, tex_size), 157 draw_controller=btn2, 158 opacity=tex_opacity * (1.0 if enabled else 0.25)) 159 if item == 'ad': 160 self._ad_button = btn2 161 self._ad_label = txt 162 assert i is not None 163 self._ad_image = i 164 self._ad_time_text = ba.textwidget( 165 parent=self._root_widget, 166 text='1m 10s', 167 position=(position[0] + size[0] * 0.5, 168 position[1] + size[1] * 0.5), 169 scale=text_scale * 1.2, 170 maxwidth=size[0] * 0.85, 171 size=(0, 0), 172 h_align='center', 173 v_align='center', 174 draw_controller=btn2, 175 color=(0.4, 0.9, 0.4, 1.0)) 176 return btn2 177 178 rsrc = self._r + '.ticketsText' 179 180 c2txt = ba.Lstr( 181 resource=rsrc, 182 subs=[('${COUNT}', 183 str(_ba.get_v1_account_misc_read_val('tickets2Amount', 184 500)))]) 185 c3txt = ba.Lstr( 186 resource=rsrc, 187 subs=[ 188 ('${COUNT}', 189 str(_ba.get_v1_account_misc_read_val('tickets3Amount', 1500))) 190 ]) 191 c4txt = ba.Lstr( 192 resource=rsrc, 193 subs=[ 194 ('${COUNT}', 195 str(_ba.get_v1_account_misc_read_val('tickets4Amount', 5000))) 196 ]) 197 c5txt = ba.Lstr( 198 resource=rsrc, 199 subs=[ 200 ('${COUNT}', 201 str(_ba.get_v1_account_misc_read_val('tickets5Amount', 202 15000))) 203 ]) 204 205 h = 110.0 206 207 # enable buttons if we have prices.. 208 tickets2_price = _ba.get_price('tickets2') 209 tickets3_price = _ba.get_price('tickets3') 210 tickets4_price = _ba.get_price('tickets4') 211 tickets5_price = _ba.get_price('tickets5') 212 213 # TEMP 214 # tickets1_price = '$0.99' 215 # tickets2_price = '$4.99' 216 # tickets3_price = '$9.99' 217 # tickets4_price = '$19.99' 218 # tickets5_price = '$49.99' 219 220 _add_button('tickets2', 221 enabled=(tickets2_price is not None), 222 position=(self._width * 0.5 - spacing * 1.5 - 223 b_size[0] * 2.0 + h, v), 224 size=b_size, 225 label=c2txt, 226 price=tickets2_price, 227 tex_name='ticketsMore') # 0.99-ish 228 _add_button('tickets3', 229 enabled=(tickets3_price is not None), 230 position=(self._width * 0.5 - spacing * 0.5 - 231 b_size[0] * 1.0 + h, v), 232 size=b_size, 233 label=c3txt, 234 price=tickets3_price, 235 tex_name='ticketRoll') # 4.99-ish 236 v -= b_size[1] - 5 237 _add_button('tickets4', 238 enabled=(tickets4_price is not None), 239 position=(self._width * 0.5 - spacing * 1.5 - 240 b_size[0] * 2.0 + h, v), 241 size=b_size, 242 label=c4txt, 243 price=tickets4_price, 244 tex_name='ticketRollBig', 245 tex_scale=1.2) # 9.99-ish 246 _add_button('tickets5', 247 enabled=(tickets5_price is not None), 248 position=(self._width * 0.5 - spacing * 0.5 - 249 b_size[0] * 1.0 + h, v), 250 size=b_size, 251 label=c5txt, 252 price=tickets5_price, 253 tex_name='ticketRolls', 254 tex_scale=1.2) # 19.99-ish 255 256 self._enable_ad_button = _ba.has_video_ads() 257 h = self._width * 0.5 + 110.0 258 v = self._height - b_size[1] - 115.0 259 260 if self._enable_ad_button: 261 h_offs = 35 262 b_size_3 = (150, 120) 263 cdb = _add_button( 264 'ad', 265 position=(h + h_offs, v), 266 size=b_size_3, 267 label=ba.Lstr(resource=self._r + '.ticketsFromASponsorText', 268 subs=[('${COUNT}', 269 str( 270 _ba.get_v1_account_misc_read_val( 271 'sponsorTickets', 5)))]), 272 tex_name='ticketsMore', 273 enabled=self._enable_ad_button, 274 tex_opacity=0.6, 275 tex_scale=0.7, 276 text_scale=0.7) 277 ba.buttonwidget(edit=cdb, 278 color=(0.65, 0.5, 279 0.7) if self._enable_ad_button else 280 (0.5, 0.5, 0.5)) 281 282 self._ad_free_text = ba.textwidget( 283 parent=self._root_widget, 284 text=ba.Lstr(resource=self._r + '.freeText'), 285 position=(h + h_offs + b_size_3[0] * 0.5, 286 v + b_size_3[1] * 0.5 + 25), 287 size=(0, 0), 288 color=(1, 1, 0, 1.0) if self._enable_ad_button else 289 (1, 1, 1, 0.2), 290 draw_controller=cdb, 291 rotate=15, 292 shadow=1.0, 293 maxwidth=150, 294 h_align='center', 295 v_align='center', 296 scale=1.0) 297 v -= 125 298 else: 299 v -= 20 300 301 if True: # pylint: disable=using-constant-test 302 h_offs = 35 303 b_size_3 = (150, 120) 304 cdb = _add_button( 305 'app_invite', 306 position=(h + h_offs, v), 307 size=b_size_3, 308 label=ba.Lstr( 309 resource='gatherWindow.earnTicketsForRecommendingText', 310 subs=[('${COUNT}', 311 str( 312 _ba.get_v1_account_misc_read_val( 313 'sponsorTickets', 5)))]), 314 tex_name='ticketsMore', 315 enabled=True, 316 tex_opacity=0.6, 317 tex_scale=0.7, 318 text_scale=0.7) 319 ba.buttonwidget(edit=cdb, color=(0.65, 0.5, 0.7)) 320 321 ba.textwidget(parent=self._root_widget, 322 text=ba.Lstr(resource=self._r + '.freeText'), 323 position=(h + h_offs + b_size_3[0] * 0.5, 324 v + b_size_3[1] * 0.5 + 25), 325 size=(0, 0), 326 color=(1, 1, 0, 1.0), 327 draw_controller=cdb, 328 rotate=15, 329 shadow=1.0, 330 maxwidth=150, 331 h_align='center', 332 v_align='center', 333 scale=1.0) 334 tc_y_offs = 0 335 336 h = self._width - (185 + x_inset) 337 v = self._height - 95 + tc_y_offs 338 339 txt1 = (ba.Lstr( 340 resource=self._r + 341 '.youHaveText').evaluate().partition('${COUNT}')[0].strip()) 342 txt2 = (ba.Lstr( 343 resource=self._r + 344 '.youHaveText').evaluate().rpartition('${COUNT}')[-1].strip()) 345 346 ba.textwidget(parent=self._root_widget, 347 text=txt1, 348 position=(h, v), 349 size=(0, 0), 350 color=(0.5, 0.5, 0.6), 351 maxwidth=200, 352 h_align='center', 353 v_align='center', 354 scale=0.8) 355 v -= 30 356 self._ticket_count_text = ba.textwidget(parent=self._root_widget, 357 position=(h, v), 358 size=(0, 0), 359 color=(0.2, 1.0, 0.2), 360 maxwidth=200, 361 h_align='center', 362 v_align='center', 363 scale=1.6) 364 v -= 30 365 ba.textwidget(parent=self._root_widget, 366 text=txt2, 367 position=(h, v), 368 size=(0, 0), 369 color=(0.5, 0.5, 0.6), 370 maxwidth=200, 371 h_align='center', 372 v_align='center', 373 scale=0.8) 374 375 # update count now and once per second going forward.. 376 self._ticking_node: ba.Node | None = None 377 self._smooth_ticket_count: float | None = None 378 self._ticket_count = 0 379 self._update() 380 self._update_timer = ba.Timer(1.0, 381 ba.WeakCall(self._update), 382 timetype=ba.TimeType.REAL, 383 repeat=True) 384 self._smooth_increase_speed = 1.0 385 386 def __del__(self) -> None: 387 if self._ticking_node is not None: 388 self._ticking_node.delete() 389 self._ticking_node = None 390 391 def _smooth_update(self) -> None: 392 if not self._ticket_count_text: 393 self._smooth_update_timer = None 394 return 395 396 finished = False 397 398 # if we're going down, do it immediately 399 assert self._smooth_ticket_count is not None 400 if int(self._smooth_ticket_count) >= self._ticket_count: 401 self._smooth_ticket_count = float(self._ticket_count) 402 finished = True 403 else: 404 # we're going up; start a sound if need be 405 self._smooth_ticket_count = min( 406 self._smooth_ticket_count + 1.0 * self._smooth_increase_speed, 407 self._ticket_count) 408 if int(self._smooth_ticket_count) >= self._ticket_count: 409 finished = True 410 self._smooth_ticket_count = float(self._ticket_count) 411 elif self._ticking_node is None: 412 with ba.Context('ui'): 413 self._ticking_node = ba.newnode( 414 'sound', 415 attrs={ 416 'sound': ba.getsound('scoreIncrease'), 417 'positional': False 418 }) 419 420 ba.textwidget(edit=self._ticket_count_text, 421 text=str(int(self._smooth_ticket_count))) 422 423 # if we've reached the target, kill the timer/sound/etc 424 if finished: 425 self._smooth_update_timer = None 426 if self._ticking_node is not None: 427 self._ticking_node.delete() 428 self._ticking_node = None 429 ba.playsound(ba.getsound('cashRegister2')) 430 431 def _update(self) -> None: 432 import datetime 433 434 # if we somehow get signed out, just die.. 435 if _ba.get_v1_account_state() != 'signed_in': 436 self._back() 437 return 438 439 self._ticket_count = _ba.get_v1_account_ticket_count() 440 441 # update our incentivized ad button depending on whether ads are 442 # available 443 if self._ad_button is not None: 444 next_reward_ad_time = _ba.get_v1_account_misc_read_val_2( 445 'nextRewardAdTime', None) 446 if next_reward_ad_time is not None: 447 next_reward_ad_time = datetime.datetime.utcfromtimestamp( 448 next_reward_ad_time) 449 now = datetime.datetime.utcnow() 450 if (_ba.have_incentivized_ad() and 451 (next_reward_ad_time is None or next_reward_ad_time <= now)): 452 self._ad_button_greyed = False 453 ba.buttonwidget(edit=self._ad_button, color=(0.65, 0.5, 0.7)) 454 ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 1.0)) 455 ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 1)) 456 ba.imagewidget(edit=self._ad_image, opacity=0.6) 457 ba.textwidget(edit=self._ad_time_text, text='') 458 else: 459 self._ad_button_greyed = True 460 ba.buttonwidget(edit=self._ad_button, color=(0.5, 0.5, 0.5)) 461 ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 0.2)) 462 ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 0.2)) 463 ba.imagewidget(edit=self._ad_image, opacity=0.6 * 0.25) 464 sval: str | ba.Lstr 465 if (next_reward_ad_time is not None 466 and next_reward_ad_time > now): 467 sval = ba.timestring( 468 (next_reward_ad_time - now).total_seconds() * 1000.0, 469 centi=False, 470 timeformat=ba.TimeFormat.MILLISECONDS) 471 else: 472 sval = '' 473 ba.textwidget(edit=self._ad_time_text, text=sval) 474 475 # if this is our first update, assign immediately; otherwise kick 476 # off a smooth transition if the value has changed 477 if self._smooth_ticket_count is None: 478 self._smooth_ticket_count = float(self._ticket_count) 479 self._smooth_update() # will set the text widget 480 481 elif (self._ticket_count != int(self._smooth_ticket_count) 482 and self._smooth_update_timer is None): 483 self._smooth_update_timer = ba.Timer(0.05, 484 ba.WeakCall( 485 self._smooth_update), 486 repeat=True, 487 timetype=ba.TimeType.REAL) 488 diff = abs(float(self._ticket_count) - self._smooth_ticket_count) 489 self._smooth_increase_speed = (diff / 490 100.0 if diff >= 5000 else diff / 491 50.0 if diff >= 1500 else diff / 492 30.0 if diff >= 500 else diff / 493 15.0) 494 495 def _disabled_press(self) -> None: 496 497 # if we're on a platform without purchases, inform the user they 498 # can link their accounts and buy stuff elsewhere 499 app = ba.app 500 if ((app.test_build or 501 (app.platform == 'android' 502 and app.subplatform in ['oculus', 'cardboard'])) 503 and _ba.get_v1_account_misc_read_val('allowAccountLinking2', 504 False)): 505 ba.screenmessage(ba.Lstr(resource=self._r + 506 '.unavailableLinkAccountText'), 507 color=(1, 0.5, 0)) 508 else: 509 ba.screenmessage(ba.Lstr(resource=self._r + '.unavailableText'), 510 color=(1, 0.5, 0)) 511 ba.playsound(ba.getsound('error')) 512 513 def _purchase(self, item: str) -> None: 514 from bastd.ui import account 515 from bastd.ui import appinvite 516 from ba.internal import master_server_get 517 if item == 'app_invite': 518 if _ba.get_v1_account_state() != 'signed_in': 519 account.show_sign_in_prompt() 520 return 521 appinvite.handle_app_invites_press() 522 return 523 # here we ping the server to ask if it's valid for us to 524 # purchase this.. (better to fail now than after we've paid locally) 525 app = ba.app 526 master_server_get('bsAccountPurchaseCheck', { 527 'item': item, 528 'platform': app.platform, 529 'subplatform': app.subplatform, 530 'version': app.version, 531 'buildNumber': app.build_number 532 }, 533 callback=ba.WeakCall(self._purchase_check_result, 534 item)) 535 536 def _purchase_check_result(self, item: str, 537 result: dict[str, Any] | None) -> None: 538 if result is None: 539 ba.playsound(ba.getsound('error')) 540 ba.screenmessage( 541 ba.Lstr(resource='internal.unavailableNoConnectionText'), 542 color=(1, 0, 0)) 543 else: 544 if result['allow']: 545 self._do_purchase(item) 546 else: 547 if result['reason'] == 'versionTooOld': 548 ba.playsound(ba.getsound('error')) 549 ba.screenmessage( 550 ba.Lstr(resource='getTicketsWindow.versionTooOldText'), 551 color=(1, 0, 0)) 552 else: 553 ba.playsound(ba.getsound('error')) 554 ba.screenmessage( 555 ba.Lstr(resource='getTicketsWindow.unavailableText'), 556 color=(1, 0, 0)) 557 558 # actually start the purchase locally.. 559 def _do_purchase(self, item: str) -> None: 560 if item == 'ad': 561 import datetime 562 # if ads are disabled until some time, error.. 563 next_reward_ad_time = _ba.get_v1_account_misc_read_val_2( 564 'nextRewardAdTime', None) 565 if next_reward_ad_time is not None: 566 next_reward_ad_time = datetime.datetime.utcfromtimestamp( 567 next_reward_ad_time) 568 now = datetime.datetime.utcnow() 569 if ((next_reward_ad_time is not None and next_reward_ad_time > now) 570 or self._ad_button_greyed): 571 ba.playsound(ba.getsound('error')) 572 ba.screenmessage(ba.Lstr( 573 resource='getTicketsWindow.unavailableTemporarilyText'), 574 color=(1, 0, 0)) 575 elif self._enable_ad_button: 576 _ba.app.ads.show_ad('tickets') 577 else: 578 _ba.purchase(item) 579 580 def _back(self) -> None: 581 from bastd.ui.store import browser 582 if self._transitioning_out: 583 return 584 ba.containerwidget(edit=self._root_widget, 585 transition=self._transition_out) 586 if not self._modal: 587 window = browser.StoreBrowserWindow( 588 transition='in_left', 589 modal=self._from_modal_store, 590 back_location=self._store_back_location).get_root_widget() 591 if not self._from_modal_store: 592 ba.app.ui.set_main_menu_window(window) 593 self._transitioning_out = True
Window for purchasing/acquiring currency.
GetCurrencyWindow( transition: str = 'in_right', from_modal_store: bool = False, modal: bool = False, origin_widget: _ba.Widget | None = None, store_back_location: str | None = None)
20 def __init__(self, 21 transition: str = 'in_right', 22 from_modal_store: bool = False, 23 modal: bool = False, 24 origin_widget: ba.Widget | None = None, 25 store_back_location: str | None = None): 26 # pylint: disable=too-many-statements 27 # pylint: disable=too-many-locals 28 29 ba.set_analytics_screen('Get Tickets Window') 30 31 self._transitioning_out = False 32 self._store_back_location = store_back_location # ew. 33 34 self._ad_button_greyed = False 35 self._smooth_update_timer: ba.Timer | None = None 36 self._ad_button = None 37 self._ad_label = None 38 self._ad_image = None 39 self._ad_time_text = None 40 41 # If they provided an origin-widget, scale up from that. 42 scale_origin: tuple[float, float] | None 43 if origin_widget is not None: 44 self._transition_out = 'out_scale' 45 scale_origin = origin_widget.get_screen_space_center() 46 transition = 'in_scale' 47 else: 48 self._transition_out = 'out_right' 49 scale_origin = None 50 51 uiscale = ba.app.ui.uiscale 52 self._width = 1000.0 if uiscale is ba.UIScale.SMALL else 800.0 53 x_inset = 100.0 if uiscale is ba.UIScale.SMALL else 0.0 54 self._height = 480.0 55 56 self._modal = modal 57 self._from_modal_store = from_modal_store 58 self._r = 'getTicketsWindow' 59 60 top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 61 62 super().__init__(root_widget=ba.containerwidget( 63 size=(self._width, self._height + top_extra), 64 transition=transition, 65 scale_origin_stack_offset=scale_origin, 66 color=(0.4, 0.37, 0.55), 67 scale=(1.63 if uiscale is ba.UIScale.SMALL else 68 1.2 if uiscale is ba.UIScale.MEDIUM else 1.0), 69 stack_offset=(0, -3) if uiscale is ba.UIScale.SMALL else (0, 0))) 70 71 btn = ba.buttonwidget( 72 parent=self._root_widget, 73 position=(55 + x_inset, self._height - 79), 74 size=(140, 60), 75 scale=1.0, 76 autoselect=True, 77 label=ba.Lstr(resource='doneText' if modal else 'backText'), 78 button_type='regular' if modal else 'back', 79 on_activate_call=self._back) 80 81 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 82 83 ba.textwidget(parent=self._root_widget, 84 position=(self._width * 0.5, self._height - 55), 85 size=(0, 0), 86 color=ba.app.ui.title_color, 87 scale=1.2, 88 h_align='center', 89 v_align='center', 90 text=ba.Lstr(resource=self._r + '.titleText'), 91 maxwidth=290) 92 93 if not modal: 94 ba.buttonwidget(edit=btn, 95 button_type='backSmall', 96 size=(60, 60), 97 label=ba.charstr(ba.SpecialChar.BACK)) 98 99 b_size = (220.0, 180.0) 100 v = self._height - b_size[1] - 80 101 spacing = 1 102 103 self._ad_button = None 104 105 def _add_button(item: str, 106 position: tuple[float, float], 107 size: tuple[float, float], 108 label: ba.Lstr, 109 price: str | None = None, 110 tex_name: str | None = None, 111 tex_opacity: float = 1.0, 112 tex_scale: float = 1.0, 113 enabled: bool = True, 114 text_scale: float = 1.0) -> ba.Widget: 115 btn2 = ba.buttonwidget( 116 parent=self._root_widget, 117 position=position, 118 button_type='square', 119 size=size, 120 label='', 121 autoselect=True, 122 color=None if enabled else (0.5, 0.5, 0.5), 123 on_activate_call=(ba.Call(self._purchase, item) 124 if enabled else self._disabled_press)) 125 txt = ba.textwidget(parent=self._root_widget, 126 text=label, 127 position=(position[0] + size[0] * 0.5, 128 position[1] + size[1] * 0.3), 129 scale=text_scale, 130 maxwidth=size[0] * 0.75, 131 size=(0, 0), 132 h_align='center', 133 v_align='center', 134 draw_controller=btn2, 135 color=(0.7, 0.9, 0.7, 1.0 if enabled else 0.2)) 136 if price is not None and enabled: 137 ba.textwidget(parent=self._root_widget, 138 text=price, 139 position=(position[0] + size[0] * 0.5, 140 position[1] + size[1] * 0.17), 141 scale=0.7, 142 maxwidth=size[0] * 0.75, 143 size=(0, 0), 144 h_align='center', 145 v_align='center', 146 draw_controller=btn2, 147 color=(0.4, 0.9, 0.4, 1.0)) 148 i = None 149 if tex_name is not None: 150 tex_size = 90.0 * tex_scale 151 i = ba.imagewidget( 152 parent=self._root_widget, 153 texture=ba.gettexture(tex_name), 154 position=(position[0] + size[0] * 0.5 - tex_size * 0.5, 155 position[1] + size[1] * 0.66 - tex_size * 0.5), 156 size=(tex_size, tex_size), 157 draw_controller=btn2, 158 opacity=tex_opacity * (1.0 if enabled else 0.25)) 159 if item == 'ad': 160 self._ad_button = btn2 161 self._ad_label = txt 162 assert i is not None 163 self._ad_image = i 164 self._ad_time_text = ba.textwidget( 165 parent=self._root_widget, 166 text='1m 10s', 167 position=(position[0] + size[0] * 0.5, 168 position[1] + size[1] * 0.5), 169 scale=text_scale * 1.2, 170 maxwidth=size[0] * 0.85, 171 size=(0, 0), 172 h_align='center', 173 v_align='center', 174 draw_controller=btn2, 175 color=(0.4, 0.9, 0.4, 1.0)) 176 return btn2 177 178 rsrc = self._r + '.ticketsText' 179 180 c2txt = ba.Lstr( 181 resource=rsrc, 182 subs=[('${COUNT}', 183 str(_ba.get_v1_account_misc_read_val('tickets2Amount', 184 500)))]) 185 c3txt = ba.Lstr( 186 resource=rsrc, 187 subs=[ 188 ('${COUNT}', 189 str(_ba.get_v1_account_misc_read_val('tickets3Amount', 1500))) 190 ]) 191 c4txt = ba.Lstr( 192 resource=rsrc, 193 subs=[ 194 ('${COUNT}', 195 str(_ba.get_v1_account_misc_read_val('tickets4Amount', 5000))) 196 ]) 197 c5txt = ba.Lstr( 198 resource=rsrc, 199 subs=[ 200 ('${COUNT}', 201 str(_ba.get_v1_account_misc_read_val('tickets5Amount', 202 15000))) 203 ]) 204 205 h = 110.0 206 207 # enable buttons if we have prices.. 208 tickets2_price = _ba.get_price('tickets2') 209 tickets3_price = _ba.get_price('tickets3') 210 tickets4_price = _ba.get_price('tickets4') 211 tickets5_price = _ba.get_price('tickets5') 212 213 # TEMP 214 # tickets1_price = '$0.99' 215 # tickets2_price = '$4.99' 216 # tickets3_price = '$9.99' 217 # tickets4_price = '$19.99' 218 # tickets5_price = '$49.99' 219 220 _add_button('tickets2', 221 enabled=(tickets2_price is not None), 222 position=(self._width * 0.5 - spacing * 1.5 - 223 b_size[0] * 2.0 + h, v), 224 size=b_size, 225 label=c2txt, 226 price=tickets2_price, 227 tex_name='ticketsMore') # 0.99-ish 228 _add_button('tickets3', 229 enabled=(tickets3_price is not None), 230 position=(self._width * 0.5 - spacing * 0.5 - 231 b_size[0] * 1.0 + h, v), 232 size=b_size, 233 label=c3txt, 234 price=tickets3_price, 235 tex_name='ticketRoll') # 4.99-ish 236 v -= b_size[1] - 5 237 _add_button('tickets4', 238 enabled=(tickets4_price is not None), 239 position=(self._width * 0.5 - spacing * 1.5 - 240 b_size[0] * 2.0 + h, v), 241 size=b_size, 242 label=c4txt, 243 price=tickets4_price, 244 tex_name='ticketRollBig', 245 tex_scale=1.2) # 9.99-ish 246 _add_button('tickets5', 247 enabled=(tickets5_price is not None), 248 position=(self._width * 0.5 - spacing * 0.5 - 249 b_size[0] * 1.0 + h, v), 250 size=b_size, 251 label=c5txt, 252 price=tickets5_price, 253 tex_name='ticketRolls', 254 tex_scale=1.2) # 19.99-ish 255 256 self._enable_ad_button = _ba.has_video_ads() 257 h = self._width * 0.5 + 110.0 258 v = self._height - b_size[1] - 115.0 259 260 if self._enable_ad_button: 261 h_offs = 35 262 b_size_3 = (150, 120) 263 cdb = _add_button( 264 'ad', 265 position=(h + h_offs, v), 266 size=b_size_3, 267 label=ba.Lstr(resource=self._r + '.ticketsFromASponsorText', 268 subs=[('${COUNT}', 269 str( 270 _ba.get_v1_account_misc_read_val( 271 'sponsorTickets', 5)))]), 272 tex_name='ticketsMore', 273 enabled=self._enable_ad_button, 274 tex_opacity=0.6, 275 tex_scale=0.7, 276 text_scale=0.7) 277 ba.buttonwidget(edit=cdb, 278 color=(0.65, 0.5, 279 0.7) if self._enable_ad_button else 280 (0.5, 0.5, 0.5)) 281 282 self._ad_free_text = ba.textwidget( 283 parent=self._root_widget, 284 text=ba.Lstr(resource=self._r + '.freeText'), 285 position=(h + h_offs + b_size_3[0] * 0.5, 286 v + b_size_3[1] * 0.5 + 25), 287 size=(0, 0), 288 color=(1, 1, 0, 1.0) if self._enable_ad_button else 289 (1, 1, 1, 0.2), 290 draw_controller=cdb, 291 rotate=15, 292 shadow=1.0, 293 maxwidth=150, 294 h_align='center', 295 v_align='center', 296 scale=1.0) 297 v -= 125 298 else: 299 v -= 20 300 301 if True: # pylint: disable=using-constant-test 302 h_offs = 35 303 b_size_3 = (150, 120) 304 cdb = _add_button( 305 'app_invite', 306 position=(h + h_offs, v), 307 size=b_size_3, 308 label=ba.Lstr( 309 resource='gatherWindow.earnTicketsForRecommendingText', 310 subs=[('${COUNT}', 311 str( 312 _ba.get_v1_account_misc_read_val( 313 'sponsorTickets', 5)))]), 314 tex_name='ticketsMore', 315 enabled=True, 316 tex_opacity=0.6, 317 tex_scale=0.7, 318 text_scale=0.7) 319 ba.buttonwidget(edit=cdb, color=(0.65, 0.5, 0.7)) 320 321 ba.textwidget(parent=self._root_widget, 322 text=ba.Lstr(resource=self._r + '.freeText'), 323 position=(h + h_offs + b_size_3[0] * 0.5, 324 v + b_size_3[1] * 0.5 + 25), 325 size=(0, 0), 326 color=(1, 1, 0, 1.0), 327 draw_controller=cdb, 328 rotate=15, 329 shadow=1.0, 330 maxwidth=150, 331 h_align='center', 332 v_align='center', 333 scale=1.0) 334 tc_y_offs = 0 335 336 h = self._width - (185 + x_inset) 337 v = self._height - 95 + tc_y_offs 338 339 txt1 = (ba.Lstr( 340 resource=self._r + 341 '.youHaveText').evaluate().partition('${COUNT}')[0].strip()) 342 txt2 = (ba.Lstr( 343 resource=self._r + 344 '.youHaveText').evaluate().rpartition('${COUNT}')[-1].strip()) 345 346 ba.textwidget(parent=self._root_widget, 347 text=txt1, 348 position=(h, v), 349 size=(0, 0), 350 color=(0.5, 0.5, 0.6), 351 maxwidth=200, 352 h_align='center', 353 v_align='center', 354 scale=0.8) 355 v -= 30 356 self._ticket_count_text = ba.textwidget(parent=self._root_widget, 357 position=(h, v), 358 size=(0, 0), 359 color=(0.2, 1.0, 0.2), 360 maxwidth=200, 361 h_align='center', 362 v_align='center', 363 scale=1.6) 364 v -= 30 365 ba.textwidget(parent=self._root_widget, 366 text=txt2, 367 position=(h, v), 368 size=(0, 0), 369 color=(0.5, 0.5, 0.6), 370 maxwidth=200, 371 h_align='center', 372 v_align='center', 373 scale=0.8) 374 375 # update count now and once per second going forward.. 376 self._ticking_node: ba.Node | None = None 377 self._smooth_ticket_count: float | None = None 378 self._ticket_count = 0 379 self._update() 380 self._update_timer = ba.Timer(1.0, 381 ba.WeakCall(self._update), 382 timetype=ba.TimeType.REAL, 383 repeat=True) 384 self._smooth_increase_speed = 1.0
Inherited Members
- ba.ui.Window
- get_root_widget
def
show_get_tickets_prompt() -> None:
596def show_get_tickets_prompt() -> None: 597 """Show a 'not enough tickets' prompt with an option to purchase more. 598 599 Note that the purchase option may not always be available 600 depending on the build of the game. 601 """ 602 from bastd.ui.confirm import ConfirmWindow 603 if ba.app.allow_ticket_purchases: 604 ConfirmWindow( 605 ba.Lstr(translate=('serverResponses', 606 'You don\'t have enough tickets for this!')), 607 lambda: GetCurrencyWindow(modal=True), 608 ok_text=ba.Lstr(resource='getTicketsWindow.titleText'), 609 width=460, 610 height=130) 611 else: 612 ConfirmWindow( 613 ba.Lstr(translate=('serverResponses', 614 'You don\'t have enough tickets for this!')), 615 cancel_button=False, 616 width=460, 617 height=130)
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.