bastd.ui.profile.edit
Provides UI to edit a player profile.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides UI to edit a player profile.""" 4 5from __future__ import annotations 6 7import random 8from typing import TYPE_CHECKING, cast 9 10import ba 11import ba.internal 12 13if TYPE_CHECKING: 14 from bastd.ui.colorpicker import ColorPicker 15 16 17class EditProfileWindow(ba.Window): 18 """Window for editing a player profile.""" 19 20 # FIXME: WILL NEED TO CHANGE THIS FOR UILOCATION. 21 def reload_window(self) -> None: 22 """Transitions out and recreates ourself.""" 23 ba.containerwidget(edit=self._root_widget, transition='out_left') 24 ba.app.ui.set_main_menu_window( 25 EditProfileWindow( 26 self.getname(), self._in_main_menu 27 ).get_root_widget() 28 ) 29 30 def __init__( 31 self, 32 existing_profile: str | None, 33 in_main_menu: bool, 34 transition: str = 'in_right', 35 ): 36 # FIXME: Tidy this up a bit. 37 # pylint: disable=too-many-branches 38 # pylint: disable=too-many-statements 39 # pylint: disable=too-many-locals 40 from ba.internal import get_player_profile_colors 41 42 self._in_main_menu = in_main_menu 43 self._existing_profile = existing_profile 44 self._r = 'editProfileWindow' 45 self._spazzes: list[str] = [] 46 self._icon_textures: list[ba.Texture] = [] 47 self._icon_tint_textures: list[ba.Texture] = [] 48 49 # Grab profile colors or pick random ones. 50 self._color, self._highlight = get_player_profile_colors( 51 existing_profile 52 ) 53 uiscale = ba.app.ui.uiscale 54 self._width = width = 780.0 if uiscale is ba.UIScale.SMALL else 680.0 55 self._x_inset = x_inset = 50.0 if uiscale is ba.UIScale.SMALL else 0.0 56 self._height = height = ( 57 350.0 58 if uiscale is ba.UIScale.SMALL 59 else 400.0 60 if uiscale is ba.UIScale.MEDIUM 61 else 450.0 62 ) 63 spacing = 40 64 self._base_scale = ( 65 2.05 66 if uiscale is ba.UIScale.SMALL 67 else 1.5 68 if uiscale is ba.UIScale.MEDIUM 69 else 1.0 70 ) 71 top_extra = 15 if uiscale is ba.UIScale.SMALL else 15 72 super().__init__( 73 root_widget=ba.containerwidget( 74 size=(width, height + top_extra), 75 transition=transition, 76 scale=self._base_scale, 77 stack_offset=(0, 15) if uiscale is ba.UIScale.SMALL else (0, 0), 78 ) 79 ) 80 cancel_button = btn = ba.buttonwidget( 81 parent=self._root_widget, 82 position=(52 + x_inset, height - 60), 83 size=(155, 60), 84 scale=0.8, 85 autoselect=True, 86 label=ba.Lstr(resource='cancelText'), 87 on_activate_call=self._cancel, 88 ) 89 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 90 save_button = btn = ba.buttonwidget( 91 parent=self._root_widget, 92 position=(width - (177 + x_inset), height - 60), 93 size=(155, 60), 94 autoselect=True, 95 scale=0.8, 96 label=ba.Lstr(resource='saveText'), 97 ) 98 ba.widget(edit=save_button, left_widget=cancel_button) 99 ba.widget(edit=cancel_button, right_widget=save_button) 100 ba.containerwidget(edit=self._root_widget, start_button=btn) 101 ba.textwidget( 102 parent=self._root_widget, 103 position=(self._width * 0.5, height - 38), 104 size=(0, 0), 105 text=( 106 ba.Lstr(resource=self._r + '.titleNewText') 107 if existing_profile is None 108 else ba.Lstr(resource=self._r + '.titleEditText') 109 ), 110 color=ba.app.ui.title_color, 111 maxwidth=290, 112 scale=1.0, 113 h_align='center', 114 v_align='center', 115 ) 116 117 # Make a list of spaz icons. 118 self.refresh_characters() 119 profile = ba.app.config.get('Player Profiles', {}).get( 120 self._existing_profile, {} 121 ) 122 123 if 'global' in profile: 124 self._global = profile['global'] 125 else: 126 self._global = False 127 128 if 'icon' in profile: 129 self._icon = profile['icon'] 130 else: 131 self._icon = ba.charstr(ba.SpecialChar.LOGO) 132 133 assigned_random_char = False 134 135 # Look for existing character choice or pick random one otherwise. 136 try: 137 icon_index = self._spazzes.index(profile['character']) 138 except Exception: 139 # Let's set the default icon to spaz for our first profile; after 140 # that we go random. 141 # (SCRATCH THAT.. we now hard-code account-profiles to start with 142 # spaz which has a similar effect) 143 # try: p_len = len(ba.app.config['Player Profiles']) 144 # except Exception: p_len = 0 145 # if p_len == 0: icon_index = self._spazzes.index('Spaz') 146 # else: 147 random.seed() 148 icon_index = random.randrange(len(self._spazzes)) 149 assigned_random_char = True 150 self._icon_index = icon_index 151 ba.buttonwidget(edit=save_button, on_activate_call=self.save) 152 153 v = height - 115.0 154 self._name = ( 155 '' if self._existing_profile is None else self._existing_profile 156 ) 157 self._is_account_profile = self._name == '__account__' 158 159 # If we just picked a random character, see if it has specific 160 # colors/highlights associated with it and assign them if so. 161 if assigned_random_char: 162 clr = ba.app.spaz_appearances[ 163 self._spazzes[icon_index] 164 ].default_color 165 if clr is not None: 166 self._color = clr 167 highlight = ba.app.spaz_appearances[ 168 self._spazzes[icon_index] 169 ].default_highlight 170 if highlight is not None: 171 self._highlight = highlight 172 173 # Assign a random name if they had none. 174 if self._name == '': 175 names = ba.internal.get_random_names() 176 self._name = names[random.randrange(len(names))] 177 178 self._clipped_name_text = ba.textwidget( 179 parent=self._root_widget, 180 text='', 181 position=(540 + x_inset, v - 8), 182 flatness=1.0, 183 shadow=0.0, 184 scale=0.55, 185 size=(0, 0), 186 maxwidth=100, 187 h_align='center', 188 v_align='center', 189 color=(1, 1, 0, 0.5), 190 ) 191 192 if not self._is_account_profile and not self._global: 193 ba.textwidget( 194 parent=self._root_widget, 195 text=ba.Lstr(resource=self._r + '.nameText'), 196 position=(200 + x_inset, v - 6), 197 size=(0, 0), 198 h_align='right', 199 v_align='center', 200 color=(1, 1, 1, 0.5), 201 scale=0.9, 202 ) 203 204 self._upgrade_button = None 205 if self._is_account_profile: 206 if ba.internal.get_v1_account_state() == 'signed_in': 207 sval = ba.internal.get_v1_account_display_string() 208 else: 209 sval = '??' 210 ba.textwidget( 211 parent=self._root_widget, 212 position=(self._width * 0.5, v - 7), 213 size=(0, 0), 214 scale=1.2, 215 text=sval, 216 maxwidth=270, 217 h_align='center', 218 v_align='center', 219 ) 220 txtl = ba.Lstr( 221 resource='editProfileWindow.accountProfileText' 222 ).evaluate() 223 b_width = min( 224 270.0, 225 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 226 ) 227 ba.textwidget( 228 parent=self._root_widget, 229 position=(self._width * 0.5, v - 39), 230 size=(0, 0), 231 scale=0.6, 232 color=ba.app.ui.infotextcolor, 233 text=txtl, 234 maxwidth=270, 235 h_align='center', 236 v_align='center', 237 ) 238 self._account_type_info_button = ba.buttonwidget( 239 parent=self._root_widget, 240 label='?', 241 size=(15, 15), 242 text_scale=0.6, 243 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47), 244 button_type='square', 245 color=(0.6, 0.5, 0.65), 246 autoselect=True, 247 on_activate_call=self.show_account_profile_info, 248 ) 249 elif self._global: 250 251 b_size = 60 252 self._icon_button = btn = ba.buttonwidget( 253 parent=self._root_widget, 254 autoselect=True, 255 position=(self._width * 0.5 - 160 - b_size * 0.5, v - 38 - 15), 256 size=(b_size, b_size), 257 color=(0.6, 0.5, 0.6), 258 label='', 259 button_type='square', 260 text_scale=1.2, 261 on_activate_call=self._on_icon_press, 262 ) 263 self._icon_button_label = ba.textwidget( 264 parent=self._root_widget, 265 position=(self._width * 0.5 - 160, v - 35), 266 draw_controller=btn, 267 h_align='center', 268 v_align='center', 269 size=(0, 0), 270 color=(1, 1, 1), 271 text='', 272 scale=2.0, 273 ) 274 275 ba.textwidget( 276 parent=self._root_widget, 277 h_align='center', 278 v_align='center', 279 position=(self._width * 0.5 - 160, v - 55 - 15), 280 size=(0, 0), 281 draw_controller=btn, 282 text=ba.Lstr(resource=self._r + '.iconText'), 283 scale=0.7, 284 color=ba.app.ui.title_color, 285 maxwidth=120, 286 ) 287 288 self._update_icon() 289 290 ba.textwidget( 291 parent=self._root_widget, 292 position=(self._width * 0.5, v - 7), 293 size=(0, 0), 294 scale=1.2, 295 text=self._name, 296 maxwidth=240, 297 h_align='center', 298 v_align='center', 299 ) 300 # FIXME hard coded strings are bad 301 txtl = ba.Lstr( 302 resource='editProfileWindow.globalProfileText' 303 ).evaluate() 304 b_width = min( 305 240.0, 306 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 307 ) 308 ba.textwidget( 309 parent=self._root_widget, 310 position=(self._width * 0.5, v - 39), 311 size=(0, 0), 312 scale=0.6, 313 color=ba.app.ui.infotextcolor, 314 text=txtl, 315 maxwidth=240, 316 h_align='center', 317 v_align='center', 318 ) 319 self._account_type_info_button = ba.buttonwidget( 320 parent=self._root_widget, 321 label='?', 322 size=(15, 15), 323 text_scale=0.6, 324 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47), 325 button_type='square', 326 color=(0.6, 0.5, 0.65), 327 autoselect=True, 328 on_activate_call=self.show_global_profile_info, 329 ) 330 else: 331 self._text_field = ba.textwidget( 332 parent=self._root_widget, 333 position=(220 + x_inset, v - 30), 334 size=(265, 40), 335 text=self._name, 336 h_align='left', 337 v_align='center', 338 max_chars=16, 339 description=ba.Lstr(resource=self._r + '.nameDescriptionText'), 340 autoselect=True, 341 editable=True, 342 padding=4, 343 color=(0.9, 0.9, 0.9, 1.0), 344 on_return_press_call=ba.Call(save_button.activate), 345 ) 346 347 # FIXME hard coded strings are bad 348 txtl = ba.Lstr( 349 resource='editProfileWindow.localProfileText' 350 ).evaluate() 351 b_width = min( 352 270.0, 353 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 354 ) 355 ba.textwidget( 356 parent=self._root_widget, 357 position=(self._width * 0.5, v - 43), 358 size=(0, 0), 359 scale=0.6, 360 color=ba.app.ui.infotextcolor, 361 text=txtl, 362 maxwidth=270, 363 h_align='center', 364 v_align='center', 365 ) 366 self._account_type_info_button = ba.buttonwidget( 367 parent=self._root_widget, 368 label='?', 369 size=(15, 15), 370 text_scale=0.6, 371 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 50), 372 button_type='square', 373 color=(0.6, 0.5, 0.65), 374 autoselect=True, 375 on_activate_call=self.show_local_profile_info, 376 ) 377 self._upgrade_button = ba.buttonwidget( 378 parent=self._root_widget, 379 label=ba.Lstr(resource='upgradeText'), 380 size=(40, 17), 381 text_scale=1.0, 382 button_type='square', 383 position=(self._width * 0.5 + b_width * 0.5 + 13 + 43, v - 51), 384 color=(0.6, 0.5, 0.65), 385 autoselect=True, 386 on_activate_call=self.upgrade_profile, 387 ) 388 389 self._update_clipped_name() 390 self._clipped_name_timer = ba.Timer( 391 0.333, 392 ba.WeakCall(self._update_clipped_name), 393 timetype=ba.TimeType.REAL, 394 repeat=True, 395 ) 396 397 v -= spacing * 3.0 398 b_size = 80 399 b_size_2 = 100 400 b_offs = 150 401 self._color_button = btn = ba.buttonwidget( 402 parent=self._root_widget, 403 autoselect=True, 404 position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50), 405 size=(b_size, b_size), 406 color=self._color, 407 label='', 408 button_type='square', 409 ) 410 origin = self._color_button.get_screen_space_center() 411 ba.buttonwidget( 412 edit=self._color_button, 413 on_activate_call=ba.WeakCall(self._make_picker, 'color', origin), 414 ) 415 ba.textwidget( 416 parent=self._root_widget, 417 h_align='center', 418 v_align='center', 419 position=(self._width * 0.5 - b_offs, v - 65), 420 size=(0, 0), 421 draw_controller=btn, 422 text=ba.Lstr(resource=self._r + '.colorText'), 423 scale=0.7, 424 color=ba.app.ui.title_color, 425 maxwidth=120, 426 ) 427 428 self._character_button = btn = ba.buttonwidget( 429 parent=self._root_widget, 430 autoselect=True, 431 position=(self._width * 0.5 - b_size_2 * 0.5, v - 60), 432 up_widget=self._account_type_info_button, 433 on_activate_call=self._on_character_press, 434 size=(b_size_2, b_size_2), 435 label='', 436 color=(1, 1, 1), 437 mask_texture=ba.gettexture('characterIconMask'), 438 ) 439 if not self._is_account_profile and not self._global: 440 ba.containerwidget( 441 edit=self._root_widget, selected_child=self._text_field 442 ) 443 ba.textwidget( 444 parent=self._root_widget, 445 h_align='center', 446 v_align='center', 447 position=(self._width * 0.5, v - 80), 448 size=(0, 0), 449 draw_controller=btn, 450 text=ba.Lstr(resource=self._r + '.characterText'), 451 scale=0.7, 452 color=ba.app.ui.title_color, 453 maxwidth=130, 454 ) 455 456 self._highlight_button = btn = ba.buttonwidget( 457 parent=self._root_widget, 458 autoselect=True, 459 position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50), 460 up_widget=self._upgrade_button 461 if self._upgrade_button is not None 462 else self._account_type_info_button, 463 size=(b_size, b_size), 464 color=self._highlight, 465 label='', 466 button_type='square', 467 ) 468 469 if not self._is_account_profile and not self._global: 470 ba.widget(edit=cancel_button, down_widget=self._text_field) 471 ba.widget(edit=save_button, down_widget=self._text_field) 472 ba.widget(edit=self._color_button, up_widget=self._text_field) 473 ba.widget( 474 edit=self._account_type_info_button, 475 down_widget=self._character_button, 476 ) 477 478 origin = self._highlight_button.get_screen_space_center() 479 ba.buttonwidget( 480 edit=self._highlight_button, 481 on_activate_call=ba.WeakCall( 482 self._make_picker, 'highlight', origin 483 ), 484 ) 485 ba.textwidget( 486 parent=self._root_widget, 487 h_align='center', 488 v_align='center', 489 position=(self._width * 0.5 + b_offs, v - 65), 490 size=(0, 0), 491 draw_controller=btn, 492 text=ba.Lstr(resource=self._r + '.highlightText'), 493 scale=0.7, 494 color=ba.app.ui.title_color, 495 maxwidth=120, 496 ) 497 self._update_character() 498 499 def upgrade_profile(self) -> None: 500 """Attempt to ugrade the profile to global.""" 501 from bastd.ui import account 502 from bastd.ui.profile import upgrade as pupgrade 503 504 if ba.internal.get_v1_account_state() != 'signed_in': 505 account.show_sign_in_prompt() 506 return 507 508 pupgrade.ProfileUpgradeWindow(self) 509 510 def show_account_profile_info(self) -> None: 511 """Show an explanation of account profiles.""" 512 from bastd.ui.confirm import ConfirmWindow 513 514 icons_str = ' '.join( 515 [ 516 ba.charstr(n) 517 for n in [ 518 ba.SpecialChar.GOOGLE_PLAY_GAMES_LOGO, 519 ba.SpecialChar.GAME_CENTER_LOGO, 520 ba.SpecialChar.GAME_CIRCLE_LOGO, 521 ba.SpecialChar.OUYA_LOGO, 522 ba.SpecialChar.LOCAL_ACCOUNT, 523 ba.SpecialChar.ALIBABA_LOGO, 524 ba.SpecialChar.OCULUS_LOGO, 525 ba.SpecialChar.NVIDIA_LOGO, 526 ] 527 ] 528 ) 529 txtl = ba.Lstr( 530 resource='editProfileWindow.accountProfileInfoText', 531 subs=[('${ICONS}', icons_str)], 532 ) 533 ConfirmWindow( 534 txtl, 535 cancel_button=False, 536 width=500, 537 height=300, 538 origin_widget=self._account_type_info_button, 539 ) 540 541 def show_local_profile_info(self) -> None: 542 """Show an explanation of local profiles.""" 543 from bastd.ui.confirm import ConfirmWindow 544 545 txtl = ba.Lstr(resource='editProfileWindow.localProfileInfoText') 546 ConfirmWindow( 547 txtl, 548 cancel_button=False, 549 width=600, 550 height=250, 551 origin_widget=self._account_type_info_button, 552 ) 553 554 def show_global_profile_info(self) -> None: 555 """Show an explanation of global profiles.""" 556 from bastd.ui.confirm import ConfirmWindow 557 558 txtl = ba.Lstr(resource='editProfileWindow.globalProfileInfoText') 559 ConfirmWindow( 560 txtl, 561 cancel_button=False, 562 width=600, 563 height=250, 564 origin_widget=self._account_type_info_button, 565 ) 566 567 def refresh_characters(self) -> None: 568 """Refresh available characters/icons.""" 569 from bastd.actor import spazappearance 570 571 self._spazzes = spazappearance.get_appearances() 572 self._spazzes.sort() 573 self._icon_textures = [ 574 ba.gettexture(ba.app.spaz_appearances[s].icon_texture) 575 for s in self._spazzes 576 ] 577 self._icon_tint_textures = [ 578 ba.gettexture(ba.app.spaz_appearances[s].icon_mask_texture) 579 for s in self._spazzes 580 ] 581 582 def on_icon_picker_pick(self, icon: str) -> None: 583 """An icon has been selected by the picker.""" 584 self._icon = icon 585 self._update_icon() 586 587 def on_character_picker_pick(self, character: str) -> None: 588 """A character has been selected by the picker.""" 589 if not self._root_widget: 590 return 591 592 # The player could have bought a new one while the picker was up. 593 self.refresh_characters() 594 self._icon_index = ( 595 self._spazzes.index(character) if character in self._spazzes else 0 596 ) 597 self._update_character() 598 599 def _on_character_press(self) -> None: 600 from bastd.ui import characterpicker 601 602 characterpicker.CharacterPicker( 603 parent=self._root_widget, 604 position=self._character_button.get_screen_space_center(), 605 selected_character=self._spazzes[self._icon_index], 606 delegate=self, 607 tint_color=self._color, 608 tint2_color=self._highlight, 609 ) 610 611 def _on_icon_press(self) -> None: 612 from bastd.ui import iconpicker 613 614 iconpicker.IconPicker( 615 parent=self._root_widget, 616 position=self._icon_button.get_screen_space_center(), 617 selected_icon=self._icon, 618 delegate=self, 619 tint_color=self._color, 620 tint2_color=self._highlight, 621 ) 622 623 def _make_picker( 624 self, picker_type: str, origin: tuple[float, float] 625 ) -> None: 626 from bastd.ui import colorpicker 627 628 if picker_type == 'color': 629 initial_color = self._color 630 elif picker_type == 'highlight': 631 initial_color = self._highlight 632 else: 633 raise ValueError('invalid picker_type: ' + picker_type) 634 colorpicker.ColorPicker( 635 parent=self._root_widget, 636 position=origin, 637 offset=( 638 self._base_scale * (-100 if picker_type == 'color' else 100), 639 0, 640 ), 641 initial_color=initial_color, 642 delegate=self, 643 tag=picker_type, 644 ) 645 646 def _cancel(self) -> None: 647 from bastd.ui.profile.browser import ProfileBrowserWindow 648 649 ba.containerwidget(edit=self._root_widget, transition='out_right') 650 ba.app.ui.set_main_menu_window( 651 ProfileBrowserWindow( 652 'in_left', 653 selected_profile=self._existing_profile, 654 in_main_menu=self._in_main_menu, 655 ).get_root_widget() 656 ) 657 658 def _set_color(self, color: tuple[float, float, float]) -> None: 659 self._color = color 660 if self._color_button: 661 ba.buttonwidget(edit=self._color_button, color=color) 662 663 def _set_highlight(self, color: tuple[float, float, float]) -> None: 664 self._highlight = color 665 if self._highlight_button: 666 ba.buttonwidget(edit=self._highlight_button, color=color) 667 668 def color_picker_closing(self, picker: ColorPicker) -> None: 669 """Called when a color picker is closing.""" 670 if not self._root_widget: 671 return 672 tag = picker.get_tag() 673 if tag == 'color': 674 ba.containerwidget( 675 edit=self._root_widget, selected_child=self._color_button 676 ) 677 elif tag == 'highlight': 678 ba.containerwidget( 679 edit=self._root_widget, selected_child=self._highlight_button 680 ) 681 else: 682 print('color_picker_closing got unknown tag ' + str(tag)) 683 684 def color_picker_selected_color( 685 self, picker: ColorPicker, color: tuple[float, float, float] 686 ) -> None: 687 """Called when a color is selected in a color picker.""" 688 if not self._root_widget: 689 return 690 tag = picker.get_tag() 691 if tag == 'color': 692 self._set_color(color) 693 elif tag == 'highlight': 694 self._set_highlight(color) 695 else: 696 print('color_picker_selected_color got unknown tag ' + str(tag)) 697 self._update_character() 698 699 def _update_clipped_name(self) -> None: 700 if not self._clipped_name_text: 701 return 702 name = self.getname() 703 if name == '__account__': 704 name = ( 705 ba.internal.get_v1_account_name() 706 if ba.internal.get_v1_account_state() == 'signed_in' 707 else '???' 708 ) 709 if len(name) > 10 and not (self._global or self._is_account_profile): 710 ba.textwidget( 711 edit=self._clipped_name_text, 712 text=ba.Lstr( 713 resource='inGameClippedNameText', 714 subs=[('${NAME}', name[:10] + '...')], 715 ), 716 ) 717 else: 718 ba.textwidget(edit=self._clipped_name_text, text='') 719 720 def _update_character(self, change: int = 0) -> None: 721 self._icon_index = (self._icon_index + change) % len(self._spazzes) 722 if self._character_button: 723 ba.buttonwidget( 724 edit=self._character_button, 725 texture=self._icon_textures[self._icon_index], 726 tint_texture=self._icon_tint_textures[self._icon_index], 727 tint_color=self._color, 728 tint2_color=self._highlight, 729 ) 730 731 def _update_icon(self) -> None: 732 if self._icon_button_label: 733 ba.textwidget(edit=self._icon_button_label, text=self._icon) 734 735 def getname(self) -> str: 736 """Return the current profile name value.""" 737 if self._is_account_profile: 738 new_name = '__account__' 739 elif self._global: 740 new_name = self._name 741 else: 742 new_name = cast(str, ba.textwidget(query=self._text_field)) 743 return new_name 744 745 def save(self, transition_out: bool = True) -> bool: 746 """Save has been selected.""" 747 from bastd.ui.profile.browser import ProfileBrowserWindow 748 749 new_name = self.getname().strip() 750 751 if not new_name: 752 ba.screenmessage(ba.Lstr(resource='nameNotEmptyText')) 753 ba.playsound(ba.getsound('error')) 754 return False 755 756 if transition_out: 757 ba.playsound(ba.getsound('gunCocking')) 758 759 # Delete old in case we're renaming. 760 if self._existing_profile and self._existing_profile != new_name: 761 ba.internal.add_transaction( 762 { 763 'type': 'REMOVE_PLAYER_PROFILE', 764 'name': self._existing_profile, 765 } 766 ) 767 768 # Also lets be aware we're no longer global if we're taking a 769 # new name (will need to re-request it). 770 self._global = False 771 772 ba.internal.add_transaction( 773 { 774 'type': 'ADD_PLAYER_PROFILE', 775 'name': new_name, 776 'profile': { 777 'character': self._spazzes[self._icon_index], 778 'color': list(self._color), 779 'global': self._global, 780 'icon': self._icon, 781 'highlight': list(self._highlight), 782 }, 783 } 784 ) 785 786 if transition_out: 787 ba.internal.run_transactions() 788 ba.containerwidget(edit=self._root_widget, transition='out_right') 789 ba.app.ui.set_main_menu_window( 790 ProfileBrowserWindow( 791 'in_left', 792 selected_profile=new_name, 793 in_main_menu=self._in_main_menu, 794 ).get_root_widget() 795 ) 796 return True
class
EditProfileWindow(ba.ui.Window):
18class EditProfileWindow(ba.Window): 19 """Window for editing a player profile.""" 20 21 # FIXME: WILL NEED TO CHANGE THIS FOR UILOCATION. 22 def reload_window(self) -> None: 23 """Transitions out and recreates ourself.""" 24 ba.containerwidget(edit=self._root_widget, transition='out_left') 25 ba.app.ui.set_main_menu_window( 26 EditProfileWindow( 27 self.getname(), self._in_main_menu 28 ).get_root_widget() 29 ) 30 31 def __init__( 32 self, 33 existing_profile: str | None, 34 in_main_menu: bool, 35 transition: str = 'in_right', 36 ): 37 # FIXME: Tidy this up a bit. 38 # pylint: disable=too-many-branches 39 # pylint: disable=too-many-statements 40 # pylint: disable=too-many-locals 41 from ba.internal import get_player_profile_colors 42 43 self._in_main_menu = in_main_menu 44 self._existing_profile = existing_profile 45 self._r = 'editProfileWindow' 46 self._spazzes: list[str] = [] 47 self._icon_textures: list[ba.Texture] = [] 48 self._icon_tint_textures: list[ba.Texture] = [] 49 50 # Grab profile colors or pick random ones. 51 self._color, self._highlight = get_player_profile_colors( 52 existing_profile 53 ) 54 uiscale = ba.app.ui.uiscale 55 self._width = width = 780.0 if uiscale is ba.UIScale.SMALL else 680.0 56 self._x_inset = x_inset = 50.0 if uiscale is ba.UIScale.SMALL else 0.0 57 self._height = height = ( 58 350.0 59 if uiscale is ba.UIScale.SMALL 60 else 400.0 61 if uiscale is ba.UIScale.MEDIUM 62 else 450.0 63 ) 64 spacing = 40 65 self._base_scale = ( 66 2.05 67 if uiscale is ba.UIScale.SMALL 68 else 1.5 69 if uiscale is ba.UIScale.MEDIUM 70 else 1.0 71 ) 72 top_extra = 15 if uiscale is ba.UIScale.SMALL else 15 73 super().__init__( 74 root_widget=ba.containerwidget( 75 size=(width, height + top_extra), 76 transition=transition, 77 scale=self._base_scale, 78 stack_offset=(0, 15) if uiscale is ba.UIScale.SMALL else (0, 0), 79 ) 80 ) 81 cancel_button = btn = ba.buttonwidget( 82 parent=self._root_widget, 83 position=(52 + x_inset, height - 60), 84 size=(155, 60), 85 scale=0.8, 86 autoselect=True, 87 label=ba.Lstr(resource='cancelText'), 88 on_activate_call=self._cancel, 89 ) 90 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 91 save_button = btn = ba.buttonwidget( 92 parent=self._root_widget, 93 position=(width - (177 + x_inset), height - 60), 94 size=(155, 60), 95 autoselect=True, 96 scale=0.8, 97 label=ba.Lstr(resource='saveText'), 98 ) 99 ba.widget(edit=save_button, left_widget=cancel_button) 100 ba.widget(edit=cancel_button, right_widget=save_button) 101 ba.containerwidget(edit=self._root_widget, start_button=btn) 102 ba.textwidget( 103 parent=self._root_widget, 104 position=(self._width * 0.5, height - 38), 105 size=(0, 0), 106 text=( 107 ba.Lstr(resource=self._r + '.titleNewText') 108 if existing_profile is None 109 else ba.Lstr(resource=self._r + '.titleEditText') 110 ), 111 color=ba.app.ui.title_color, 112 maxwidth=290, 113 scale=1.0, 114 h_align='center', 115 v_align='center', 116 ) 117 118 # Make a list of spaz icons. 119 self.refresh_characters() 120 profile = ba.app.config.get('Player Profiles', {}).get( 121 self._existing_profile, {} 122 ) 123 124 if 'global' in profile: 125 self._global = profile['global'] 126 else: 127 self._global = False 128 129 if 'icon' in profile: 130 self._icon = profile['icon'] 131 else: 132 self._icon = ba.charstr(ba.SpecialChar.LOGO) 133 134 assigned_random_char = False 135 136 # Look for existing character choice or pick random one otherwise. 137 try: 138 icon_index = self._spazzes.index(profile['character']) 139 except Exception: 140 # Let's set the default icon to spaz for our first profile; after 141 # that we go random. 142 # (SCRATCH THAT.. we now hard-code account-profiles to start with 143 # spaz which has a similar effect) 144 # try: p_len = len(ba.app.config['Player Profiles']) 145 # except Exception: p_len = 0 146 # if p_len == 0: icon_index = self._spazzes.index('Spaz') 147 # else: 148 random.seed() 149 icon_index = random.randrange(len(self._spazzes)) 150 assigned_random_char = True 151 self._icon_index = icon_index 152 ba.buttonwidget(edit=save_button, on_activate_call=self.save) 153 154 v = height - 115.0 155 self._name = ( 156 '' if self._existing_profile is None else self._existing_profile 157 ) 158 self._is_account_profile = self._name == '__account__' 159 160 # If we just picked a random character, see if it has specific 161 # colors/highlights associated with it and assign them if so. 162 if assigned_random_char: 163 clr = ba.app.spaz_appearances[ 164 self._spazzes[icon_index] 165 ].default_color 166 if clr is not None: 167 self._color = clr 168 highlight = ba.app.spaz_appearances[ 169 self._spazzes[icon_index] 170 ].default_highlight 171 if highlight is not None: 172 self._highlight = highlight 173 174 # Assign a random name if they had none. 175 if self._name == '': 176 names = ba.internal.get_random_names() 177 self._name = names[random.randrange(len(names))] 178 179 self._clipped_name_text = ba.textwidget( 180 parent=self._root_widget, 181 text='', 182 position=(540 + x_inset, v - 8), 183 flatness=1.0, 184 shadow=0.0, 185 scale=0.55, 186 size=(0, 0), 187 maxwidth=100, 188 h_align='center', 189 v_align='center', 190 color=(1, 1, 0, 0.5), 191 ) 192 193 if not self._is_account_profile and not self._global: 194 ba.textwidget( 195 parent=self._root_widget, 196 text=ba.Lstr(resource=self._r + '.nameText'), 197 position=(200 + x_inset, v - 6), 198 size=(0, 0), 199 h_align='right', 200 v_align='center', 201 color=(1, 1, 1, 0.5), 202 scale=0.9, 203 ) 204 205 self._upgrade_button = None 206 if self._is_account_profile: 207 if ba.internal.get_v1_account_state() == 'signed_in': 208 sval = ba.internal.get_v1_account_display_string() 209 else: 210 sval = '??' 211 ba.textwidget( 212 parent=self._root_widget, 213 position=(self._width * 0.5, v - 7), 214 size=(0, 0), 215 scale=1.2, 216 text=sval, 217 maxwidth=270, 218 h_align='center', 219 v_align='center', 220 ) 221 txtl = ba.Lstr( 222 resource='editProfileWindow.accountProfileText' 223 ).evaluate() 224 b_width = min( 225 270.0, 226 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 227 ) 228 ba.textwidget( 229 parent=self._root_widget, 230 position=(self._width * 0.5, v - 39), 231 size=(0, 0), 232 scale=0.6, 233 color=ba.app.ui.infotextcolor, 234 text=txtl, 235 maxwidth=270, 236 h_align='center', 237 v_align='center', 238 ) 239 self._account_type_info_button = ba.buttonwidget( 240 parent=self._root_widget, 241 label='?', 242 size=(15, 15), 243 text_scale=0.6, 244 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47), 245 button_type='square', 246 color=(0.6, 0.5, 0.65), 247 autoselect=True, 248 on_activate_call=self.show_account_profile_info, 249 ) 250 elif self._global: 251 252 b_size = 60 253 self._icon_button = btn = ba.buttonwidget( 254 parent=self._root_widget, 255 autoselect=True, 256 position=(self._width * 0.5 - 160 - b_size * 0.5, v - 38 - 15), 257 size=(b_size, b_size), 258 color=(0.6, 0.5, 0.6), 259 label='', 260 button_type='square', 261 text_scale=1.2, 262 on_activate_call=self._on_icon_press, 263 ) 264 self._icon_button_label = ba.textwidget( 265 parent=self._root_widget, 266 position=(self._width * 0.5 - 160, v - 35), 267 draw_controller=btn, 268 h_align='center', 269 v_align='center', 270 size=(0, 0), 271 color=(1, 1, 1), 272 text='', 273 scale=2.0, 274 ) 275 276 ba.textwidget( 277 parent=self._root_widget, 278 h_align='center', 279 v_align='center', 280 position=(self._width * 0.5 - 160, v - 55 - 15), 281 size=(0, 0), 282 draw_controller=btn, 283 text=ba.Lstr(resource=self._r + '.iconText'), 284 scale=0.7, 285 color=ba.app.ui.title_color, 286 maxwidth=120, 287 ) 288 289 self._update_icon() 290 291 ba.textwidget( 292 parent=self._root_widget, 293 position=(self._width * 0.5, v - 7), 294 size=(0, 0), 295 scale=1.2, 296 text=self._name, 297 maxwidth=240, 298 h_align='center', 299 v_align='center', 300 ) 301 # FIXME hard coded strings are bad 302 txtl = ba.Lstr( 303 resource='editProfileWindow.globalProfileText' 304 ).evaluate() 305 b_width = min( 306 240.0, 307 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 308 ) 309 ba.textwidget( 310 parent=self._root_widget, 311 position=(self._width * 0.5, v - 39), 312 size=(0, 0), 313 scale=0.6, 314 color=ba.app.ui.infotextcolor, 315 text=txtl, 316 maxwidth=240, 317 h_align='center', 318 v_align='center', 319 ) 320 self._account_type_info_button = ba.buttonwidget( 321 parent=self._root_widget, 322 label='?', 323 size=(15, 15), 324 text_scale=0.6, 325 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47), 326 button_type='square', 327 color=(0.6, 0.5, 0.65), 328 autoselect=True, 329 on_activate_call=self.show_global_profile_info, 330 ) 331 else: 332 self._text_field = ba.textwidget( 333 parent=self._root_widget, 334 position=(220 + x_inset, v - 30), 335 size=(265, 40), 336 text=self._name, 337 h_align='left', 338 v_align='center', 339 max_chars=16, 340 description=ba.Lstr(resource=self._r + '.nameDescriptionText'), 341 autoselect=True, 342 editable=True, 343 padding=4, 344 color=(0.9, 0.9, 0.9, 1.0), 345 on_return_press_call=ba.Call(save_button.activate), 346 ) 347 348 # FIXME hard coded strings are bad 349 txtl = ba.Lstr( 350 resource='editProfileWindow.localProfileText' 351 ).evaluate() 352 b_width = min( 353 270.0, 354 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 355 ) 356 ba.textwidget( 357 parent=self._root_widget, 358 position=(self._width * 0.5, v - 43), 359 size=(0, 0), 360 scale=0.6, 361 color=ba.app.ui.infotextcolor, 362 text=txtl, 363 maxwidth=270, 364 h_align='center', 365 v_align='center', 366 ) 367 self._account_type_info_button = ba.buttonwidget( 368 parent=self._root_widget, 369 label='?', 370 size=(15, 15), 371 text_scale=0.6, 372 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 50), 373 button_type='square', 374 color=(0.6, 0.5, 0.65), 375 autoselect=True, 376 on_activate_call=self.show_local_profile_info, 377 ) 378 self._upgrade_button = ba.buttonwidget( 379 parent=self._root_widget, 380 label=ba.Lstr(resource='upgradeText'), 381 size=(40, 17), 382 text_scale=1.0, 383 button_type='square', 384 position=(self._width * 0.5 + b_width * 0.5 + 13 + 43, v - 51), 385 color=(0.6, 0.5, 0.65), 386 autoselect=True, 387 on_activate_call=self.upgrade_profile, 388 ) 389 390 self._update_clipped_name() 391 self._clipped_name_timer = ba.Timer( 392 0.333, 393 ba.WeakCall(self._update_clipped_name), 394 timetype=ba.TimeType.REAL, 395 repeat=True, 396 ) 397 398 v -= spacing * 3.0 399 b_size = 80 400 b_size_2 = 100 401 b_offs = 150 402 self._color_button = btn = ba.buttonwidget( 403 parent=self._root_widget, 404 autoselect=True, 405 position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50), 406 size=(b_size, b_size), 407 color=self._color, 408 label='', 409 button_type='square', 410 ) 411 origin = self._color_button.get_screen_space_center() 412 ba.buttonwidget( 413 edit=self._color_button, 414 on_activate_call=ba.WeakCall(self._make_picker, 'color', origin), 415 ) 416 ba.textwidget( 417 parent=self._root_widget, 418 h_align='center', 419 v_align='center', 420 position=(self._width * 0.5 - b_offs, v - 65), 421 size=(0, 0), 422 draw_controller=btn, 423 text=ba.Lstr(resource=self._r + '.colorText'), 424 scale=0.7, 425 color=ba.app.ui.title_color, 426 maxwidth=120, 427 ) 428 429 self._character_button = btn = ba.buttonwidget( 430 parent=self._root_widget, 431 autoselect=True, 432 position=(self._width * 0.5 - b_size_2 * 0.5, v - 60), 433 up_widget=self._account_type_info_button, 434 on_activate_call=self._on_character_press, 435 size=(b_size_2, b_size_2), 436 label='', 437 color=(1, 1, 1), 438 mask_texture=ba.gettexture('characterIconMask'), 439 ) 440 if not self._is_account_profile and not self._global: 441 ba.containerwidget( 442 edit=self._root_widget, selected_child=self._text_field 443 ) 444 ba.textwidget( 445 parent=self._root_widget, 446 h_align='center', 447 v_align='center', 448 position=(self._width * 0.5, v - 80), 449 size=(0, 0), 450 draw_controller=btn, 451 text=ba.Lstr(resource=self._r + '.characterText'), 452 scale=0.7, 453 color=ba.app.ui.title_color, 454 maxwidth=130, 455 ) 456 457 self._highlight_button = btn = ba.buttonwidget( 458 parent=self._root_widget, 459 autoselect=True, 460 position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50), 461 up_widget=self._upgrade_button 462 if self._upgrade_button is not None 463 else self._account_type_info_button, 464 size=(b_size, b_size), 465 color=self._highlight, 466 label='', 467 button_type='square', 468 ) 469 470 if not self._is_account_profile and not self._global: 471 ba.widget(edit=cancel_button, down_widget=self._text_field) 472 ba.widget(edit=save_button, down_widget=self._text_field) 473 ba.widget(edit=self._color_button, up_widget=self._text_field) 474 ba.widget( 475 edit=self._account_type_info_button, 476 down_widget=self._character_button, 477 ) 478 479 origin = self._highlight_button.get_screen_space_center() 480 ba.buttonwidget( 481 edit=self._highlight_button, 482 on_activate_call=ba.WeakCall( 483 self._make_picker, 'highlight', origin 484 ), 485 ) 486 ba.textwidget( 487 parent=self._root_widget, 488 h_align='center', 489 v_align='center', 490 position=(self._width * 0.5 + b_offs, v - 65), 491 size=(0, 0), 492 draw_controller=btn, 493 text=ba.Lstr(resource=self._r + '.highlightText'), 494 scale=0.7, 495 color=ba.app.ui.title_color, 496 maxwidth=120, 497 ) 498 self._update_character() 499 500 def upgrade_profile(self) -> None: 501 """Attempt to ugrade the profile to global.""" 502 from bastd.ui import account 503 from bastd.ui.profile import upgrade as pupgrade 504 505 if ba.internal.get_v1_account_state() != 'signed_in': 506 account.show_sign_in_prompt() 507 return 508 509 pupgrade.ProfileUpgradeWindow(self) 510 511 def show_account_profile_info(self) -> None: 512 """Show an explanation of account profiles.""" 513 from bastd.ui.confirm import ConfirmWindow 514 515 icons_str = ' '.join( 516 [ 517 ba.charstr(n) 518 for n in [ 519 ba.SpecialChar.GOOGLE_PLAY_GAMES_LOGO, 520 ba.SpecialChar.GAME_CENTER_LOGO, 521 ba.SpecialChar.GAME_CIRCLE_LOGO, 522 ba.SpecialChar.OUYA_LOGO, 523 ba.SpecialChar.LOCAL_ACCOUNT, 524 ba.SpecialChar.ALIBABA_LOGO, 525 ba.SpecialChar.OCULUS_LOGO, 526 ba.SpecialChar.NVIDIA_LOGO, 527 ] 528 ] 529 ) 530 txtl = ba.Lstr( 531 resource='editProfileWindow.accountProfileInfoText', 532 subs=[('${ICONS}', icons_str)], 533 ) 534 ConfirmWindow( 535 txtl, 536 cancel_button=False, 537 width=500, 538 height=300, 539 origin_widget=self._account_type_info_button, 540 ) 541 542 def show_local_profile_info(self) -> None: 543 """Show an explanation of local profiles.""" 544 from bastd.ui.confirm import ConfirmWindow 545 546 txtl = ba.Lstr(resource='editProfileWindow.localProfileInfoText') 547 ConfirmWindow( 548 txtl, 549 cancel_button=False, 550 width=600, 551 height=250, 552 origin_widget=self._account_type_info_button, 553 ) 554 555 def show_global_profile_info(self) -> None: 556 """Show an explanation of global profiles.""" 557 from bastd.ui.confirm import ConfirmWindow 558 559 txtl = ba.Lstr(resource='editProfileWindow.globalProfileInfoText') 560 ConfirmWindow( 561 txtl, 562 cancel_button=False, 563 width=600, 564 height=250, 565 origin_widget=self._account_type_info_button, 566 ) 567 568 def refresh_characters(self) -> None: 569 """Refresh available characters/icons.""" 570 from bastd.actor import spazappearance 571 572 self._spazzes = spazappearance.get_appearances() 573 self._spazzes.sort() 574 self._icon_textures = [ 575 ba.gettexture(ba.app.spaz_appearances[s].icon_texture) 576 for s in self._spazzes 577 ] 578 self._icon_tint_textures = [ 579 ba.gettexture(ba.app.spaz_appearances[s].icon_mask_texture) 580 for s in self._spazzes 581 ] 582 583 def on_icon_picker_pick(self, icon: str) -> None: 584 """An icon has been selected by the picker.""" 585 self._icon = icon 586 self._update_icon() 587 588 def on_character_picker_pick(self, character: str) -> None: 589 """A character has been selected by the picker.""" 590 if not self._root_widget: 591 return 592 593 # The player could have bought a new one while the picker was up. 594 self.refresh_characters() 595 self._icon_index = ( 596 self._spazzes.index(character) if character in self._spazzes else 0 597 ) 598 self._update_character() 599 600 def _on_character_press(self) -> None: 601 from bastd.ui import characterpicker 602 603 characterpicker.CharacterPicker( 604 parent=self._root_widget, 605 position=self._character_button.get_screen_space_center(), 606 selected_character=self._spazzes[self._icon_index], 607 delegate=self, 608 tint_color=self._color, 609 tint2_color=self._highlight, 610 ) 611 612 def _on_icon_press(self) -> None: 613 from bastd.ui import iconpicker 614 615 iconpicker.IconPicker( 616 parent=self._root_widget, 617 position=self._icon_button.get_screen_space_center(), 618 selected_icon=self._icon, 619 delegate=self, 620 tint_color=self._color, 621 tint2_color=self._highlight, 622 ) 623 624 def _make_picker( 625 self, picker_type: str, origin: tuple[float, float] 626 ) -> None: 627 from bastd.ui import colorpicker 628 629 if picker_type == 'color': 630 initial_color = self._color 631 elif picker_type == 'highlight': 632 initial_color = self._highlight 633 else: 634 raise ValueError('invalid picker_type: ' + picker_type) 635 colorpicker.ColorPicker( 636 parent=self._root_widget, 637 position=origin, 638 offset=( 639 self._base_scale * (-100 if picker_type == 'color' else 100), 640 0, 641 ), 642 initial_color=initial_color, 643 delegate=self, 644 tag=picker_type, 645 ) 646 647 def _cancel(self) -> None: 648 from bastd.ui.profile.browser import ProfileBrowserWindow 649 650 ba.containerwidget(edit=self._root_widget, transition='out_right') 651 ba.app.ui.set_main_menu_window( 652 ProfileBrowserWindow( 653 'in_left', 654 selected_profile=self._existing_profile, 655 in_main_menu=self._in_main_menu, 656 ).get_root_widget() 657 ) 658 659 def _set_color(self, color: tuple[float, float, float]) -> None: 660 self._color = color 661 if self._color_button: 662 ba.buttonwidget(edit=self._color_button, color=color) 663 664 def _set_highlight(self, color: tuple[float, float, float]) -> None: 665 self._highlight = color 666 if self._highlight_button: 667 ba.buttonwidget(edit=self._highlight_button, color=color) 668 669 def color_picker_closing(self, picker: ColorPicker) -> None: 670 """Called when a color picker is closing.""" 671 if not self._root_widget: 672 return 673 tag = picker.get_tag() 674 if tag == 'color': 675 ba.containerwidget( 676 edit=self._root_widget, selected_child=self._color_button 677 ) 678 elif tag == 'highlight': 679 ba.containerwidget( 680 edit=self._root_widget, selected_child=self._highlight_button 681 ) 682 else: 683 print('color_picker_closing got unknown tag ' + str(tag)) 684 685 def color_picker_selected_color( 686 self, picker: ColorPicker, color: tuple[float, float, float] 687 ) -> None: 688 """Called when a color is selected in a color picker.""" 689 if not self._root_widget: 690 return 691 tag = picker.get_tag() 692 if tag == 'color': 693 self._set_color(color) 694 elif tag == 'highlight': 695 self._set_highlight(color) 696 else: 697 print('color_picker_selected_color got unknown tag ' + str(tag)) 698 self._update_character() 699 700 def _update_clipped_name(self) -> None: 701 if not self._clipped_name_text: 702 return 703 name = self.getname() 704 if name == '__account__': 705 name = ( 706 ba.internal.get_v1_account_name() 707 if ba.internal.get_v1_account_state() == 'signed_in' 708 else '???' 709 ) 710 if len(name) > 10 and not (self._global or self._is_account_profile): 711 ba.textwidget( 712 edit=self._clipped_name_text, 713 text=ba.Lstr( 714 resource='inGameClippedNameText', 715 subs=[('${NAME}', name[:10] + '...')], 716 ), 717 ) 718 else: 719 ba.textwidget(edit=self._clipped_name_text, text='') 720 721 def _update_character(self, change: int = 0) -> None: 722 self._icon_index = (self._icon_index + change) % len(self._spazzes) 723 if self._character_button: 724 ba.buttonwidget( 725 edit=self._character_button, 726 texture=self._icon_textures[self._icon_index], 727 tint_texture=self._icon_tint_textures[self._icon_index], 728 tint_color=self._color, 729 tint2_color=self._highlight, 730 ) 731 732 def _update_icon(self) -> None: 733 if self._icon_button_label: 734 ba.textwidget(edit=self._icon_button_label, text=self._icon) 735 736 def getname(self) -> str: 737 """Return the current profile name value.""" 738 if self._is_account_profile: 739 new_name = '__account__' 740 elif self._global: 741 new_name = self._name 742 else: 743 new_name = cast(str, ba.textwidget(query=self._text_field)) 744 return new_name 745 746 def save(self, transition_out: bool = True) -> bool: 747 """Save has been selected.""" 748 from bastd.ui.profile.browser import ProfileBrowserWindow 749 750 new_name = self.getname().strip() 751 752 if not new_name: 753 ba.screenmessage(ba.Lstr(resource='nameNotEmptyText')) 754 ba.playsound(ba.getsound('error')) 755 return False 756 757 if transition_out: 758 ba.playsound(ba.getsound('gunCocking')) 759 760 # Delete old in case we're renaming. 761 if self._existing_profile and self._existing_profile != new_name: 762 ba.internal.add_transaction( 763 { 764 'type': 'REMOVE_PLAYER_PROFILE', 765 'name': self._existing_profile, 766 } 767 ) 768 769 # Also lets be aware we're no longer global if we're taking a 770 # new name (will need to re-request it). 771 self._global = False 772 773 ba.internal.add_transaction( 774 { 775 'type': 'ADD_PLAYER_PROFILE', 776 'name': new_name, 777 'profile': { 778 'character': self._spazzes[self._icon_index], 779 'color': list(self._color), 780 'global': self._global, 781 'icon': self._icon, 782 'highlight': list(self._highlight), 783 }, 784 } 785 ) 786 787 if transition_out: 788 ba.internal.run_transactions() 789 ba.containerwidget(edit=self._root_widget, transition='out_right') 790 ba.app.ui.set_main_menu_window( 791 ProfileBrowserWindow( 792 'in_left', 793 selected_profile=new_name, 794 in_main_menu=self._in_main_menu, 795 ).get_root_widget() 796 ) 797 return True
Window for editing a player profile.
EditProfileWindow( existing_profile: str | None, in_main_menu: bool, transition: str = 'in_right')
31 def __init__( 32 self, 33 existing_profile: str | None, 34 in_main_menu: bool, 35 transition: str = 'in_right', 36 ): 37 # FIXME: Tidy this up a bit. 38 # pylint: disable=too-many-branches 39 # pylint: disable=too-many-statements 40 # pylint: disable=too-many-locals 41 from ba.internal import get_player_profile_colors 42 43 self._in_main_menu = in_main_menu 44 self._existing_profile = existing_profile 45 self._r = 'editProfileWindow' 46 self._spazzes: list[str] = [] 47 self._icon_textures: list[ba.Texture] = [] 48 self._icon_tint_textures: list[ba.Texture] = [] 49 50 # Grab profile colors or pick random ones. 51 self._color, self._highlight = get_player_profile_colors( 52 existing_profile 53 ) 54 uiscale = ba.app.ui.uiscale 55 self._width = width = 780.0 if uiscale is ba.UIScale.SMALL else 680.0 56 self._x_inset = x_inset = 50.0 if uiscale is ba.UIScale.SMALL else 0.0 57 self._height = height = ( 58 350.0 59 if uiscale is ba.UIScale.SMALL 60 else 400.0 61 if uiscale is ba.UIScale.MEDIUM 62 else 450.0 63 ) 64 spacing = 40 65 self._base_scale = ( 66 2.05 67 if uiscale is ba.UIScale.SMALL 68 else 1.5 69 if uiscale is ba.UIScale.MEDIUM 70 else 1.0 71 ) 72 top_extra = 15 if uiscale is ba.UIScale.SMALL else 15 73 super().__init__( 74 root_widget=ba.containerwidget( 75 size=(width, height + top_extra), 76 transition=transition, 77 scale=self._base_scale, 78 stack_offset=(0, 15) if uiscale is ba.UIScale.SMALL else (0, 0), 79 ) 80 ) 81 cancel_button = btn = ba.buttonwidget( 82 parent=self._root_widget, 83 position=(52 + x_inset, height - 60), 84 size=(155, 60), 85 scale=0.8, 86 autoselect=True, 87 label=ba.Lstr(resource='cancelText'), 88 on_activate_call=self._cancel, 89 ) 90 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 91 save_button = btn = ba.buttonwidget( 92 parent=self._root_widget, 93 position=(width - (177 + x_inset), height - 60), 94 size=(155, 60), 95 autoselect=True, 96 scale=0.8, 97 label=ba.Lstr(resource='saveText'), 98 ) 99 ba.widget(edit=save_button, left_widget=cancel_button) 100 ba.widget(edit=cancel_button, right_widget=save_button) 101 ba.containerwidget(edit=self._root_widget, start_button=btn) 102 ba.textwidget( 103 parent=self._root_widget, 104 position=(self._width * 0.5, height - 38), 105 size=(0, 0), 106 text=( 107 ba.Lstr(resource=self._r + '.titleNewText') 108 if existing_profile is None 109 else ba.Lstr(resource=self._r + '.titleEditText') 110 ), 111 color=ba.app.ui.title_color, 112 maxwidth=290, 113 scale=1.0, 114 h_align='center', 115 v_align='center', 116 ) 117 118 # Make a list of spaz icons. 119 self.refresh_characters() 120 profile = ba.app.config.get('Player Profiles', {}).get( 121 self._existing_profile, {} 122 ) 123 124 if 'global' in profile: 125 self._global = profile['global'] 126 else: 127 self._global = False 128 129 if 'icon' in profile: 130 self._icon = profile['icon'] 131 else: 132 self._icon = ba.charstr(ba.SpecialChar.LOGO) 133 134 assigned_random_char = False 135 136 # Look for existing character choice or pick random one otherwise. 137 try: 138 icon_index = self._spazzes.index(profile['character']) 139 except Exception: 140 # Let's set the default icon to spaz for our first profile; after 141 # that we go random. 142 # (SCRATCH THAT.. we now hard-code account-profiles to start with 143 # spaz which has a similar effect) 144 # try: p_len = len(ba.app.config['Player Profiles']) 145 # except Exception: p_len = 0 146 # if p_len == 0: icon_index = self._spazzes.index('Spaz') 147 # else: 148 random.seed() 149 icon_index = random.randrange(len(self._spazzes)) 150 assigned_random_char = True 151 self._icon_index = icon_index 152 ba.buttonwidget(edit=save_button, on_activate_call=self.save) 153 154 v = height - 115.0 155 self._name = ( 156 '' if self._existing_profile is None else self._existing_profile 157 ) 158 self._is_account_profile = self._name == '__account__' 159 160 # If we just picked a random character, see if it has specific 161 # colors/highlights associated with it and assign them if so. 162 if assigned_random_char: 163 clr = ba.app.spaz_appearances[ 164 self._spazzes[icon_index] 165 ].default_color 166 if clr is not None: 167 self._color = clr 168 highlight = ba.app.spaz_appearances[ 169 self._spazzes[icon_index] 170 ].default_highlight 171 if highlight is not None: 172 self._highlight = highlight 173 174 # Assign a random name if they had none. 175 if self._name == '': 176 names = ba.internal.get_random_names() 177 self._name = names[random.randrange(len(names))] 178 179 self._clipped_name_text = ba.textwidget( 180 parent=self._root_widget, 181 text='', 182 position=(540 + x_inset, v - 8), 183 flatness=1.0, 184 shadow=0.0, 185 scale=0.55, 186 size=(0, 0), 187 maxwidth=100, 188 h_align='center', 189 v_align='center', 190 color=(1, 1, 0, 0.5), 191 ) 192 193 if not self._is_account_profile and not self._global: 194 ba.textwidget( 195 parent=self._root_widget, 196 text=ba.Lstr(resource=self._r + '.nameText'), 197 position=(200 + x_inset, v - 6), 198 size=(0, 0), 199 h_align='right', 200 v_align='center', 201 color=(1, 1, 1, 0.5), 202 scale=0.9, 203 ) 204 205 self._upgrade_button = None 206 if self._is_account_profile: 207 if ba.internal.get_v1_account_state() == 'signed_in': 208 sval = ba.internal.get_v1_account_display_string() 209 else: 210 sval = '??' 211 ba.textwidget( 212 parent=self._root_widget, 213 position=(self._width * 0.5, v - 7), 214 size=(0, 0), 215 scale=1.2, 216 text=sval, 217 maxwidth=270, 218 h_align='center', 219 v_align='center', 220 ) 221 txtl = ba.Lstr( 222 resource='editProfileWindow.accountProfileText' 223 ).evaluate() 224 b_width = min( 225 270.0, 226 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 227 ) 228 ba.textwidget( 229 parent=self._root_widget, 230 position=(self._width * 0.5, v - 39), 231 size=(0, 0), 232 scale=0.6, 233 color=ba.app.ui.infotextcolor, 234 text=txtl, 235 maxwidth=270, 236 h_align='center', 237 v_align='center', 238 ) 239 self._account_type_info_button = ba.buttonwidget( 240 parent=self._root_widget, 241 label='?', 242 size=(15, 15), 243 text_scale=0.6, 244 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47), 245 button_type='square', 246 color=(0.6, 0.5, 0.65), 247 autoselect=True, 248 on_activate_call=self.show_account_profile_info, 249 ) 250 elif self._global: 251 252 b_size = 60 253 self._icon_button = btn = ba.buttonwidget( 254 parent=self._root_widget, 255 autoselect=True, 256 position=(self._width * 0.5 - 160 - b_size * 0.5, v - 38 - 15), 257 size=(b_size, b_size), 258 color=(0.6, 0.5, 0.6), 259 label='', 260 button_type='square', 261 text_scale=1.2, 262 on_activate_call=self._on_icon_press, 263 ) 264 self._icon_button_label = ba.textwidget( 265 parent=self._root_widget, 266 position=(self._width * 0.5 - 160, v - 35), 267 draw_controller=btn, 268 h_align='center', 269 v_align='center', 270 size=(0, 0), 271 color=(1, 1, 1), 272 text='', 273 scale=2.0, 274 ) 275 276 ba.textwidget( 277 parent=self._root_widget, 278 h_align='center', 279 v_align='center', 280 position=(self._width * 0.5 - 160, v - 55 - 15), 281 size=(0, 0), 282 draw_controller=btn, 283 text=ba.Lstr(resource=self._r + '.iconText'), 284 scale=0.7, 285 color=ba.app.ui.title_color, 286 maxwidth=120, 287 ) 288 289 self._update_icon() 290 291 ba.textwidget( 292 parent=self._root_widget, 293 position=(self._width * 0.5, v - 7), 294 size=(0, 0), 295 scale=1.2, 296 text=self._name, 297 maxwidth=240, 298 h_align='center', 299 v_align='center', 300 ) 301 # FIXME hard coded strings are bad 302 txtl = ba.Lstr( 303 resource='editProfileWindow.globalProfileText' 304 ).evaluate() 305 b_width = min( 306 240.0, 307 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 308 ) 309 ba.textwidget( 310 parent=self._root_widget, 311 position=(self._width * 0.5, v - 39), 312 size=(0, 0), 313 scale=0.6, 314 color=ba.app.ui.infotextcolor, 315 text=txtl, 316 maxwidth=240, 317 h_align='center', 318 v_align='center', 319 ) 320 self._account_type_info_button = ba.buttonwidget( 321 parent=self._root_widget, 322 label='?', 323 size=(15, 15), 324 text_scale=0.6, 325 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47), 326 button_type='square', 327 color=(0.6, 0.5, 0.65), 328 autoselect=True, 329 on_activate_call=self.show_global_profile_info, 330 ) 331 else: 332 self._text_field = ba.textwidget( 333 parent=self._root_widget, 334 position=(220 + x_inset, v - 30), 335 size=(265, 40), 336 text=self._name, 337 h_align='left', 338 v_align='center', 339 max_chars=16, 340 description=ba.Lstr(resource=self._r + '.nameDescriptionText'), 341 autoselect=True, 342 editable=True, 343 padding=4, 344 color=(0.9, 0.9, 0.9, 1.0), 345 on_return_press_call=ba.Call(save_button.activate), 346 ) 347 348 # FIXME hard coded strings are bad 349 txtl = ba.Lstr( 350 resource='editProfileWindow.localProfileText' 351 ).evaluate() 352 b_width = min( 353 270.0, 354 ba.internal.get_string_width(txtl, suppress_warning=True) * 0.6, 355 ) 356 ba.textwidget( 357 parent=self._root_widget, 358 position=(self._width * 0.5, v - 43), 359 size=(0, 0), 360 scale=0.6, 361 color=ba.app.ui.infotextcolor, 362 text=txtl, 363 maxwidth=270, 364 h_align='center', 365 v_align='center', 366 ) 367 self._account_type_info_button = ba.buttonwidget( 368 parent=self._root_widget, 369 label='?', 370 size=(15, 15), 371 text_scale=0.6, 372 position=(self._width * 0.5 + b_width * 0.5 + 13, v - 50), 373 button_type='square', 374 color=(0.6, 0.5, 0.65), 375 autoselect=True, 376 on_activate_call=self.show_local_profile_info, 377 ) 378 self._upgrade_button = ba.buttonwidget( 379 parent=self._root_widget, 380 label=ba.Lstr(resource='upgradeText'), 381 size=(40, 17), 382 text_scale=1.0, 383 button_type='square', 384 position=(self._width * 0.5 + b_width * 0.5 + 13 + 43, v - 51), 385 color=(0.6, 0.5, 0.65), 386 autoselect=True, 387 on_activate_call=self.upgrade_profile, 388 ) 389 390 self._update_clipped_name() 391 self._clipped_name_timer = ba.Timer( 392 0.333, 393 ba.WeakCall(self._update_clipped_name), 394 timetype=ba.TimeType.REAL, 395 repeat=True, 396 ) 397 398 v -= spacing * 3.0 399 b_size = 80 400 b_size_2 = 100 401 b_offs = 150 402 self._color_button = btn = ba.buttonwidget( 403 parent=self._root_widget, 404 autoselect=True, 405 position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50), 406 size=(b_size, b_size), 407 color=self._color, 408 label='', 409 button_type='square', 410 ) 411 origin = self._color_button.get_screen_space_center() 412 ba.buttonwidget( 413 edit=self._color_button, 414 on_activate_call=ba.WeakCall(self._make_picker, 'color', origin), 415 ) 416 ba.textwidget( 417 parent=self._root_widget, 418 h_align='center', 419 v_align='center', 420 position=(self._width * 0.5 - b_offs, v - 65), 421 size=(0, 0), 422 draw_controller=btn, 423 text=ba.Lstr(resource=self._r + '.colorText'), 424 scale=0.7, 425 color=ba.app.ui.title_color, 426 maxwidth=120, 427 ) 428 429 self._character_button = btn = ba.buttonwidget( 430 parent=self._root_widget, 431 autoselect=True, 432 position=(self._width * 0.5 - b_size_2 * 0.5, v - 60), 433 up_widget=self._account_type_info_button, 434 on_activate_call=self._on_character_press, 435 size=(b_size_2, b_size_2), 436 label='', 437 color=(1, 1, 1), 438 mask_texture=ba.gettexture('characterIconMask'), 439 ) 440 if not self._is_account_profile and not self._global: 441 ba.containerwidget( 442 edit=self._root_widget, selected_child=self._text_field 443 ) 444 ba.textwidget( 445 parent=self._root_widget, 446 h_align='center', 447 v_align='center', 448 position=(self._width * 0.5, v - 80), 449 size=(0, 0), 450 draw_controller=btn, 451 text=ba.Lstr(resource=self._r + '.characterText'), 452 scale=0.7, 453 color=ba.app.ui.title_color, 454 maxwidth=130, 455 ) 456 457 self._highlight_button = btn = ba.buttonwidget( 458 parent=self._root_widget, 459 autoselect=True, 460 position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50), 461 up_widget=self._upgrade_button 462 if self._upgrade_button is not None 463 else self._account_type_info_button, 464 size=(b_size, b_size), 465 color=self._highlight, 466 label='', 467 button_type='square', 468 ) 469 470 if not self._is_account_profile and not self._global: 471 ba.widget(edit=cancel_button, down_widget=self._text_field) 472 ba.widget(edit=save_button, down_widget=self._text_field) 473 ba.widget(edit=self._color_button, up_widget=self._text_field) 474 ba.widget( 475 edit=self._account_type_info_button, 476 down_widget=self._character_button, 477 ) 478 479 origin = self._highlight_button.get_screen_space_center() 480 ba.buttonwidget( 481 edit=self._highlight_button, 482 on_activate_call=ba.WeakCall( 483 self._make_picker, 'highlight', origin 484 ), 485 ) 486 ba.textwidget( 487 parent=self._root_widget, 488 h_align='center', 489 v_align='center', 490 position=(self._width * 0.5 + b_offs, v - 65), 491 size=(0, 0), 492 draw_controller=btn, 493 text=ba.Lstr(resource=self._r + '.highlightText'), 494 scale=0.7, 495 color=ba.app.ui.title_color, 496 maxwidth=120, 497 ) 498 self._update_character()
def
reload_window(self) -> None:
22 def reload_window(self) -> None: 23 """Transitions out and recreates ourself.""" 24 ba.containerwidget(edit=self._root_widget, transition='out_left') 25 ba.app.ui.set_main_menu_window( 26 EditProfileWindow( 27 self.getname(), self._in_main_menu 28 ).get_root_widget() 29 )
Transitions out and recreates ourself.
def
upgrade_profile(self) -> None:
500 def upgrade_profile(self) -> None: 501 """Attempt to ugrade the profile to global.""" 502 from bastd.ui import account 503 from bastd.ui.profile import upgrade as pupgrade 504 505 if ba.internal.get_v1_account_state() != 'signed_in': 506 account.show_sign_in_prompt() 507 return 508 509 pupgrade.ProfileUpgradeWindow(self)
Attempt to ugrade the profile to global.
def
show_account_profile_info(self) -> None:
511 def show_account_profile_info(self) -> None: 512 """Show an explanation of account profiles.""" 513 from bastd.ui.confirm import ConfirmWindow 514 515 icons_str = ' '.join( 516 [ 517 ba.charstr(n) 518 for n in [ 519 ba.SpecialChar.GOOGLE_PLAY_GAMES_LOGO, 520 ba.SpecialChar.GAME_CENTER_LOGO, 521 ba.SpecialChar.GAME_CIRCLE_LOGO, 522 ba.SpecialChar.OUYA_LOGO, 523 ba.SpecialChar.LOCAL_ACCOUNT, 524 ba.SpecialChar.ALIBABA_LOGO, 525 ba.SpecialChar.OCULUS_LOGO, 526 ba.SpecialChar.NVIDIA_LOGO, 527 ] 528 ] 529 ) 530 txtl = ba.Lstr( 531 resource='editProfileWindow.accountProfileInfoText', 532 subs=[('${ICONS}', icons_str)], 533 ) 534 ConfirmWindow( 535 txtl, 536 cancel_button=False, 537 width=500, 538 height=300, 539 origin_widget=self._account_type_info_button, 540 )
Show an explanation of account profiles.
def
show_local_profile_info(self) -> None:
542 def show_local_profile_info(self) -> None: 543 """Show an explanation of local profiles.""" 544 from bastd.ui.confirm import ConfirmWindow 545 546 txtl = ba.Lstr(resource='editProfileWindow.localProfileInfoText') 547 ConfirmWindow( 548 txtl, 549 cancel_button=False, 550 width=600, 551 height=250, 552 origin_widget=self._account_type_info_button, 553 )
Show an explanation of local profiles.
def
show_global_profile_info(self) -> None:
555 def show_global_profile_info(self) -> None: 556 """Show an explanation of global profiles.""" 557 from bastd.ui.confirm import ConfirmWindow 558 559 txtl = ba.Lstr(resource='editProfileWindow.globalProfileInfoText') 560 ConfirmWindow( 561 txtl, 562 cancel_button=False, 563 width=600, 564 height=250, 565 origin_widget=self._account_type_info_button, 566 )
Show an explanation of global profiles.
def
refresh_characters(self) -> None:
568 def refresh_characters(self) -> None: 569 """Refresh available characters/icons.""" 570 from bastd.actor import spazappearance 571 572 self._spazzes = spazappearance.get_appearances() 573 self._spazzes.sort() 574 self._icon_textures = [ 575 ba.gettexture(ba.app.spaz_appearances[s].icon_texture) 576 for s in self._spazzes 577 ] 578 self._icon_tint_textures = [ 579 ba.gettexture(ba.app.spaz_appearances[s].icon_mask_texture) 580 for s in self._spazzes 581 ]
Refresh available characters/icons.
def
on_icon_picker_pick(self, icon: str) -> None:
583 def on_icon_picker_pick(self, icon: str) -> None: 584 """An icon has been selected by the picker.""" 585 self._icon = icon 586 self._update_icon()
An icon has been selected by the picker.
def
on_character_picker_pick(self, character: str) -> None:
588 def on_character_picker_pick(self, character: str) -> None: 589 """A character has been selected by the picker.""" 590 if not self._root_widget: 591 return 592 593 # The player could have bought a new one while the picker was up. 594 self.refresh_characters() 595 self._icon_index = ( 596 self._spazzes.index(character) if character in self._spazzes else 0 597 ) 598 self._update_character()
A character has been selected by the picker.
669 def color_picker_closing(self, picker: ColorPicker) -> None: 670 """Called when a color picker is closing.""" 671 if not self._root_widget: 672 return 673 tag = picker.get_tag() 674 if tag == 'color': 675 ba.containerwidget( 676 edit=self._root_widget, selected_child=self._color_button 677 ) 678 elif tag == 'highlight': 679 ba.containerwidget( 680 edit=self._root_widget, selected_child=self._highlight_button 681 ) 682 else: 683 print('color_picker_closing got unknown tag ' + str(tag))
Called when a color picker is closing.
def
color_picker_selected_color( self, picker: bastd.ui.colorpicker.ColorPicker, color: tuple[float, float, float]) -> None:
685 def color_picker_selected_color( 686 self, picker: ColorPicker, color: tuple[float, float, float] 687 ) -> None: 688 """Called when a color is selected in a color picker.""" 689 if not self._root_widget: 690 return 691 tag = picker.get_tag() 692 if tag == 'color': 693 self._set_color(color) 694 elif tag == 'highlight': 695 self._set_highlight(color) 696 else: 697 print('color_picker_selected_color got unknown tag ' + str(tag)) 698 self._update_character()
Called when a color is selected in a color picker.
def
getname(self) -> str:
736 def getname(self) -> str: 737 """Return the current profile name value.""" 738 if self._is_account_profile: 739 new_name = '__account__' 740 elif self._global: 741 new_name = self._name 742 else: 743 new_name = cast(str, ba.textwidget(query=self._text_field)) 744 return new_name
Return the current profile name value.
def
save(self, transition_out: bool = True) -> bool:
746 def save(self, transition_out: bool = True) -> bool: 747 """Save has been selected.""" 748 from bastd.ui.profile.browser import ProfileBrowserWindow 749 750 new_name = self.getname().strip() 751 752 if not new_name: 753 ba.screenmessage(ba.Lstr(resource='nameNotEmptyText')) 754 ba.playsound(ba.getsound('error')) 755 return False 756 757 if transition_out: 758 ba.playsound(ba.getsound('gunCocking')) 759 760 # Delete old in case we're renaming. 761 if self._existing_profile and self._existing_profile != new_name: 762 ba.internal.add_transaction( 763 { 764 'type': 'REMOVE_PLAYER_PROFILE', 765 'name': self._existing_profile, 766 } 767 ) 768 769 # Also lets be aware we're no longer global if we're taking a 770 # new name (will need to re-request it). 771 self._global = False 772 773 ba.internal.add_transaction( 774 { 775 'type': 'ADD_PLAYER_PROFILE', 776 'name': new_name, 777 'profile': { 778 'character': self._spazzes[self._icon_index], 779 'color': list(self._color), 780 'global': self._global, 781 'icon': self._icon, 782 'highlight': list(self._highlight), 783 }, 784 } 785 ) 786 787 if transition_out: 788 ba.internal.run_transactions() 789 ba.containerwidget(edit=self._root_widget, transition='out_right') 790 ba.app.ui.set_main_menu_window( 791 ProfileBrowserWindow( 792 'in_left', 793 selected_profile=new_name, 794 in_main_menu=self._in_main_menu, 795 ).get_root_widget() 796 ) 797 return True
Save has been selected.
Inherited Members
- ba.ui.Window
- get_root_widget