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