bastd.ui.profile.upgrade
UI for player profile upgrades.
1# Released under the MIT License. See LICENSE for details. 2# 3"""UI for player profile upgrades.""" 4 5from __future__ import annotations 6 7import time 8import weakref 9from typing import TYPE_CHECKING 10 11import ba 12import ba.internal 13 14if TYPE_CHECKING: 15 from typing import Any 16 from bastd.ui.profile.edit import EditProfileWindow 17 18 19class ProfileUpgradeWindow(ba.Window): 20 """Window for player profile upgrades to global.""" 21 22 def __init__( 23 self, 24 edit_profile_window: EditProfileWindow, 25 transition: str = 'in_right', 26 ): 27 from ba.internal import master_server_get 28 29 self._r = 'editProfileWindow' 30 31 self._width = 680 32 self._height = 350 33 uiscale = ba.app.ui.uiscale 34 self._base_scale = ( 35 2.05 36 if uiscale is ba.UIScale.SMALL 37 else 1.5 38 if uiscale is ba.UIScale.MEDIUM 39 else 1.2 40 ) 41 self._upgrade_start_time: float | None = None 42 self._name = edit_profile_window.getname() 43 self._edit_profile_window = weakref.ref(edit_profile_window) 44 45 top_extra = 15 if uiscale is ba.UIScale.SMALL else 15 46 super().__init__( 47 root_widget=ba.containerwidget( 48 size=(self._width, self._height + top_extra), 49 toolbar_visibility='menu_currency', 50 transition=transition, 51 scale=self._base_scale, 52 stack_offset=(0, 15) if uiscale is ba.UIScale.SMALL else (0, 0), 53 ) 54 ) 55 cancel_button = ba.buttonwidget( 56 parent=self._root_widget, 57 position=(52, 30), 58 size=(155, 60), 59 scale=0.8, 60 autoselect=True, 61 label=ba.Lstr(resource='cancelText'), 62 on_activate_call=self._cancel, 63 ) 64 self._upgrade_button = ba.buttonwidget( 65 parent=self._root_widget, 66 position=(self._width - 190, 30), 67 size=(155, 60), 68 scale=0.8, 69 autoselect=True, 70 label=ba.Lstr(resource='upgradeText'), 71 on_activate_call=self._on_upgrade_press, 72 ) 73 ba.containerwidget( 74 edit=self._root_widget, 75 cancel_button=cancel_button, 76 start_button=self._upgrade_button, 77 selected_child=self._upgrade_button, 78 ) 79 80 ba.textwidget( 81 parent=self._root_widget, 82 position=(self._width * 0.5, self._height - 38), 83 size=(0, 0), 84 text=ba.Lstr(resource=self._r + '.upgradeToGlobalProfileText'), 85 color=ba.app.ui.title_color, 86 maxwidth=self._width * 0.45, 87 scale=1.0, 88 h_align='center', 89 v_align='center', 90 ) 91 92 ba.textwidget( 93 parent=self._root_widget, 94 position=(self._width * 0.5, self._height - 100), 95 size=(0, 0), 96 text=ba.Lstr(resource=self._r + '.upgradeProfileInfoText'), 97 color=ba.app.ui.infotextcolor, 98 maxwidth=self._width * 0.8, 99 scale=0.7, 100 h_align='center', 101 v_align='center', 102 ) 103 104 self._status_text = ba.textwidget( 105 parent=self._root_widget, 106 position=(self._width * 0.5, self._height - 160), 107 size=(0, 0), 108 text=ba.Lstr( 109 resource=self._r + '.checkingAvailabilityText', 110 subs=[('${NAME}', self._name)], 111 ), 112 color=(0.8, 0.4, 0.0), 113 maxwidth=self._width * 0.8, 114 scale=0.65, 115 h_align='center', 116 v_align='center', 117 ) 118 119 self._price_text = ba.textwidget( 120 parent=self._root_widget, 121 position=(self._width * 0.5, self._height - 230), 122 size=(0, 0), 123 text='', 124 color=(0.2, 1, 0.2), 125 maxwidth=self._width * 0.8, 126 scale=1.5, 127 h_align='center', 128 v_align='center', 129 ) 130 131 self._tickets_text: ba.Widget | None 132 if not ba.app.ui.use_toolbars: 133 self._tickets_text = ba.textwidget( 134 parent=self._root_widget, 135 position=(self._width * 0.9 - 5, self._height - 30), 136 size=(0, 0), 137 text=ba.charstr(ba.SpecialChar.TICKET) + '123', 138 color=(0.2, 1, 0.2), 139 maxwidth=100, 140 scale=0.5, 141 h_align='right', 142 v_align='center', 143 ) 144 else: 145 self._tickets_text = None 146 147 master_server_get( 148 'bsGlobalProfileCheck', 149 {'name': self._name, 'b': ba.app.build_number}, 150 callback=ba.WeakCall(self._profile_check_result), 151 ) 152 self._cost = ba.internal.get_v1_account_misc_read_val( 153 'price.global_profile', 500 154 ) 155 self._status: str | None = 'waiting' 156 self._update_timer = ba.Timer( 157 1.0, 158 ba.WeakCall(self._update), 159 timetype=ba.TimeType.REAL, 160 repeat=True, 161 ) 162 self._update() 163 164 def _profile_check_result(self, result: dict[str, Any] | None) -> None: 165 if result is None: 166 ba.textwidget( 167 edit=self._status_text, 168 text=ba.Lstr(resource='internal.unavailableNoConnectionText'), 169 color=(1, 0, 0), 170 ) 171 self._status = 'error' 172 ba.buttonwidget( 173 edit=self._upgrade_button, 174 color=(0.4, 0.4, 0.4), 175 textcolor=(0.5, 0.5, 0.5), 176 ) 177 else: 178 if result['available']: 179 ba.textwidget( 180 edit=self._status_text, 181 text=ba.Lstr( 182 resource=self._r + '.availableText', 183 subs=[('${NAME}', self._name)], 184 ), 185 color=(0, 1, 0), 186 ) 187 ba.textwidget( 188 edit=self._price_text, 189 text=ba.charstr(ba.SpecialChar.TICKET) + str(self._cost), 190 ) 191 self._status = None 192 else: 193 ba.textwidget( 194 edit=self._status_text, 195 text=ba.Lstr( 196 resource=self._r + '.unavailableText', 197 subs=[('${NAME}', self._name)], 198 ), 199 color=(1, 0, 0), 200 ) 201 self._status = 'unavailable' 202 ba.buttonwidget( 203 edit=self._upgrade_button, 204 color=(0.4, 0.4, 0.4), 205 textcolor=(0.5, 0.5, 0.5), 206 ) 207 208 def _on_upgrade_press(self) -> None: 209 from bastd.ui import getcurrency 210 211 if self._status is None: 212 # If it appears we don't have enough tickets, offer to buy more. 213 tickets = ba.internal.get_v1_account_ticket_count() 214 if tickets < self._cost: 215 ba.playsound(ba.getsound('error')) 216 getcurrency.show_get_tickets_prompt() 217 return 218 ba.screenmessage( 219 ba.Lstr(resource='purchasingText'), color=(0, 1, 0) 220 ) 221 self._status = 'pre_upgrading' 222 223 # Now we tell the original editor to save the profile, add an 224 # upgrade transaction, and then sit and wait for everything to 225 # go through. 226 edit_profile_window = self._edit_profile_window() 227 if edit_profile_window is None: 228 print('profile upgrade: original edit window gone') 229 return 230 success = edit_profile_window.save(transition_out=False) 231 if not success: 232 print('profile upgrade: error occurred saving profile') 233 ba.screenmessage(ba.Lstr(resource='errorText'), color=(1, 0, 0)) 234 ba.playsound(ba.getsound('error')) 235 return 236 ba.internal.add_transaction( 237 {'type': 'UPGRADE_PROFILE', 'name': self._name} 238 ) 239 ba.internal.run_transactions() 240 self._status = 'upgrading' 241 self._upgrade_start_time = time.time() 242 else: 243 ba.playsound(ba.getsound('error')) 244 245 def _update(self) -> None: 246 try: 247 t_str = str(ba.internal.get_v1_account_ticket_count()) 248 except Exception: 249 t_str = '?' 250 if self._tickets_text is not None: 251 ba.textwidget( 252 edit=self._tickets_text, 253 text=ba.Lstr( 254 resource='getTicketsWindow.youHaveShortText', 255 subs=[ 256 ('${COUNT}', ba.charstr(ba.SpecialChar.TICKET) + t_str) 257 ], 258 ), 259 ) 260 261 # Once we've kicked off an upgrade attempt and all transactions go 262 # through, we're done. 263 if ( 264 self._status == 'upgrading' 265 and not ba.internal.have_outstanding_transactions() 266 ): 267 self._status = 'exiting' 268 ba.containerwidget(edit=self._root_widget, transition='out_right') 269 edit_profile_window = self._edit_profile_window() 270 if edit_profile_window is None: 271 print( 272 'profile upgrade transition out:' 273 ' original edit window gone' 274 ) 275 return 276 ba.playsound(ba.getsound('gunCocking')) 277 edit_profile_window.reload_window() 278 279 def _cancel(self) -> None: 280 # If we recently sent out an upgrade request, disallow canceling 281 # for a bit. 282 if ( 283 self._upgrade_start_time is not None 284 and time.time() - self._upgrade_start_time < 10.0 285 ): 286 ba.playsound(ba.getsound('error')) 287 return 288 ba.containerwidget(edit=self._root_widget, transition='out_right')
class
ProfileUpgradeWindow(ba.ui.Window):
20class ProfileUpgradeWindow(ba.Window): 21 """Window for player profile upgrades to global.""" 22 23 def __init__( 24 self, 25 edit_profile_window: EditProfileWindow, 26 transition: str = 'in_right', 27 ): 28 from ba.internal import master_server_get 29 30 self._r = 'editProfileWindow' 31 32 self._width = 680 33 self._height = 350 34 uiscale = ba.app.ui.uiscale 35 self._base_scale = ( 36 2.05 37 if uiscale is ba.UIScale.SMALL 38 else 1.5 39 if uiscale is ba.UIScale.MEDIUM 40 else 1.2 41 ) 42 self._upgrade_start_time: float | None = None 43 self._name = edit_profile_window.getname() 44 self._edit_profile_window = weakref.ref(edit_profile_window) 45 46 top_extra = 15 if uiscale is ba.UIScale.SMALL else 15 47 super().__init__( 48 root_widget=ba.containerwidget( 49 size=(self._width, self._height + top_extra), 50 toolbar_visibility='menu_currency', 51 transition=transition, 52 scale=self._base_scale, 53 stack_offset=(0, 15) if uiscale is ba.UIScale.SMALL else (0, 0), 54 ) 55 ) 56 cancel_button = ba.buttonwidget( 57 parent=self._root_widget, 58 position=(52, 30), 59 size=(155, 60), 60 scale=0.8, 61 autoselect=True, 62 label=ba.Lstr(resource='cancelText'), 63 on_activate_call=self._cancel, 64 ) 65 self._upgrade_button = ba.buttonwidget( 66 parent=self._root_widget, 67 position=(self._width - 190, 30), 68 size=(155, 60), 69 scale=0.8, 70 autoselect=True, 71 label=ba.Lstr(resource='upgradeText'), 72 on_activate_call=self._on_upgrade_press, 73 ) 74 ba.containerwidget( 75 edit=self._root_widget, 76 cancel_button=cancel_button, 77 start_button=self._upgrade_button, 78 selected_child=self._upgrade_button, 79 ) 80 81 ba.textwidget( 82 parent=self._root_widget, 83 position=(self._width * 0.5, self._height - 38), 84 size=(0, 0), 85 text=ba.Lstr(resource=self._r + '.upgradeToGlobalProfileText'), 86 color=ba.app.ui.title_color, 87 maxwidth=self._width * 0.45, 88 scale=1.0, 89 h_align='center', 90 v_align='center', 91 ) 92 93 ba.textwidget( 94 parent=self._root_widget, 95 position=(self._width * 0.5, self._height - 100), 96 size=(0, 0), 97 text=ba.Lstr(resource=self._r + '.upgradeProfileInfoText'), 98 color=ba.app.ui.infotextcolor, 99 maxwidth=self._width * 0.8, 100 scale=0.7, 101 h_align='center', 102 v_align='center', 103 ) 104 105 self._status_text = ba.textwidget( 106 parent=self._root_widget, 107 position=(self._width * 0.5, self._height - 160), 108 size=(0, 0), 109 text=ba.Lstr( 110 resource=self._r + '.checkingAvailabilityText', 111 subs=[('${NAME}', self._name)], 112 ), 113 color=(0.8, 0.4, 0.0), 114 maxwidth=self._width * 0.8, 115 scale=0.65, 116 h_align='center', 117 v_align='center', 118 ) 119 120 self._price_text = ba.textwidget( 121 parent=self._root_widget, 122 position=(self._width * 0.5, self._height - 230), 123 size=(0, 0), 124 text='', 125 color=(0.2, 1, 0.2), 126 maxwidth=self._width * 0.8, 127 scale=1.5, 128 h_align='center', 129 v_align='center', 130 ) 131 132 self._tickets_text: ba.Widget | None 133 if not ba.app.ui.use_toolbars: 134 self._tickets_text = ba.textwidget( 135 parent=self._root_widget, 136 position=(self._width * 0.9 - 5, self._height - 30), 137 size=(0, 0), 138 text=ba.charstr(ba.SpecialChar.TICKET) + '123', 139 color=(0.2, 1, 0.2), 140 maxwidth=100, 141 scale=0.5, 142 h_align='right', 143 v_align='center', 144 ) 145 else: 146 self._tickets_text = None 147 148 master_server_get( 149 'bsGlobalProfileCheck', 150 {'name': self._name, 'b': ba.app.build_number}, 151 callback=ba.WeakCall(self._profile_check_result), 152 ) 153 self._cost = ba.internal.get_v1_account_misc_read_val( 154 'price.global_profile', 500 155 ) 156 self._status: str | None = 'waiting' 157 self._update_timer = ba.Timer( 158 1.0, 159 ba.WeakCall(self._update), 160 timetype=ba.TimeType.REAL, 161 repeat=True, 162 ) 163 self._update() 164 165 def _profile_check_result(self, result: dict[str, Any] | None) -> None: 166 if result is None: 167 ba.textwidget( 168 edit=self._status_text, 169 text=ba.Lstr(resource='internal.unavailableNoConnectionText'), 170 color=(1, 0, 0), 171 ) 172 self._status = 'error' 173 ba.buttonwidget( 174 edit=self._upgrade_button, 175 color=(0.4, 0.4, 0.4), 176 textcolor=(0.5, 0.5, 0.5), 177 ) 178 else: 179 if result['available']: 180 ba.textwidget( 181 edit=self._status_text, 182 text=ba.Lstr( 183 resource=self._r + '.availableText', 184 subs=[('${NAME}', self._name)], 185 ), 186 color=(0, 1, 0), 187 ) 188 ba.textwidget( 189 edit=self._price_text, 190 text=ba.charstr(ba.SpecialChar.TICKET) + str(self._cost), 191 ) 192 self._status = None 193 else: 194 ba.textwidget( 195 edit=self._status_text, 196 text=ba.Lstr( 197 resource=self._r + '.unavailableText', 198 subs=[('${NAME}', self._name)], 199 ), 200 color=(1, 0, 0), 201 ) 202 self._status = 'unavailable' 203 ba.buttonwidget( 204 edit=self._upgrade_button, 205 color=(0.4, 0.4, 0.4), 206 textcolor=(0.5, 0.5, 0.5), 207 ) 208 209 def _on_upgrade_press(self) -> None: 210 from bastd.ui import getcurrency 211 212 if self._status is None: 213 # If it appears we don't have enough tickets, offer to buy more. 214 tickets = ba.internal.get_v1_account_ticket_count() 215 if tickets < self._cost: 216 ba.playsound(ba.getsound('error')) 217 getcurrency.show_get_tickets_prompt() 218 return 219 ba.screenmessage( 220 ba.Lstr(resource='purchasingText'), color=(0, 1, 0) 221 ) 222 self._status = 'pre_upgrading' 223 224 # Now we tell the original editor to save the profile, add an 225 # upgrade transaction, and then sit and wait for everything to 226 # go through. 227 edit_profile_window = self._edit_profile_window() 228 if edit_profile_window is None: 229 print('profile upgrade: original edit window gone') 230 return 231 success = edit_profile_window.save(transition_out=False) 232 if not success: 233 print('profile upgrade: error occurred saving profile') 234 ba.screenmessage(ba.Lstr(resource='errorText'), color=(1, 0, 0)) 235 ba.playsound(ba.getsound('error')) 236 return 237 ba.internal.add_transaction( 238 {'type': 'UPGRADE_PROFILE', 'name': self._name} 239 ) 240 ba.internal.run_transactions() 241 self._status = 'upgrading' 242 self._upgrade_start_time = time.time() 243 else: 244 ba.playsound(ba.getsound('error')) 245 246 def _update(self) -> None: 247 try: 248 t_str = str(ba.internal.get_v1_account_ticket_count()) 249 except Exception: 250 t_str = '?' 251 if self._tickets_text is not None: 252 ba.textwidget( 253 edit=self._tickets_text, 254 text=ba.Lstr( 255 resource='getTicketsWindow.youHaveShortText', 256 subs=[ 257 ('${COUNT}', ba.charstr(ba.SpecialChar.TICKET) + t_str) 258 ], 259 ), 260 ) 261 262 # Once we've kicked off an upgrade attempt and all transactions go 263 # through, we're done. 264 if ( 265 self._status == 'upgrading' 266 and not ba.internal.have_outstanding_transactions() 267 ): 268 self._status = 'exiting' 269 ba.containerwidget(edit=self._root_widget, transition='out_right') 270 edit_profile_window = self._edit_profile_window() 271 if edit_profile_window is None: 272 print( 273 'profile upgrade transition out:' 274 ' original edit window gone' 275 ) 276 return 277 ba.playsound(ba.getsound('gunCocking')) 278 edit_profile_window.reload_window() 279 280 def _cancel(self) -> None: 281 # If we recently sent out an upgrade request, disallow canceling 282 # for a bit. 283 if ( 284 self._upgrade_start_time is not None 285 and time.time() - self._upgrade_start_time < 10.0 286 ): 287 ba.playsound(ba.getsound('error')) 288 return 289 ba.containerwidget(edit=self._root_widget, transition='out_right')
Window for player profile upgrades to global.
ProfileUpgradeWindow( edit_profile_window: bastd.ui.profile.edit.EditProfileWindow, transition: str = 'in_right')
23 def __init__( 24 self, 25 edit_profile_window: EditProfileWindow, 26 transition: str = 'in_right', 27 ): 28 from ba.internal import master_server_get 29 30 self._r = 'editProfileWindow' 31 32 self._width = 680 33 self._height = 350 34 uiscale = ba.app.ui.uiscale 35 self._base_scale = ( 36 2.05 37 if uiscale is ba.UIScale.SMALL 38 else 1.5 39 if uiscale is ba.UIScale.MEDIUM 40 else 1.2 41 ) 42 self._upgrade_start_time: float | None = None 43 self._name = edit_profile_window.getname() 44 self._edit_profile_window = weakref.ref(edit_profile_window) 45 46 top_extra = 15 if uiscale is ba.UIScale.SMALL else 15 47 super().__init__( 48 root_widget=ba.containerwidget( 49 size=(self._width, self._height + top_extra), 50 toolbar_visibility='menu_currency', 51 transition=transition, 52 scale=self._base_scale, 53 stack_offset=(0, 15) if uiscale is ba.UIScale.SMALL else (0, 0), 54 ) 55 ) 56 cancel_button = ba.buttonwidget( 57 parent=self._root_widget, 58 position=(52, 30), 59 size=(155, 60), 60 scale=0.8, 61 autoselect=True, 62 label=ba.Lstr(resource='cancelText'), 63 on_activate_call=self._cancel, 64 ) 65 self._upgrade_button = ba.buttonwidget( 66 parent=self._root_widget, 67 position=(self._width - 190, 30), 68 size=(155, 60), 69 scale=0.8, 70 autoselect=True, 71 label=ba.Lstr(resource='upgradeText'), 72 on_activate_call=self._on_upgrade_press, 73 ) 74 ba.containerwidget( 75 edit=self._root_widget, 76 cancel_button=cancel_button, 77 start_button=self._upgrade_button, 78 selected_child=self._upgrade_button, 79 ) 80 81 ba.textwidget( 82 parent=self._root_widget, 83 position=(self._width * 0.5, self._height - 38), 84 size=(0, 0), 85 text=ba.Lstr(resource=self._r + '.upgradeToGlobalProfileText'), 86 color=ba.app.ui.title_color, 87 maxwidth=self._width * 0.45, 88 scale=1.0, 89 h_align='center', 90 v_align='center', 91 ) 92 93 ba.textwidget( 94 parent=self._root_widget, 95 position=(self._width * 0.5, self._height - 100), 96 size=(0, 0), 97 text=ba.Lstr(resource=self._r + '.upgradeProfileInfoText'), 98 color=ba.app.ui.infotextcolor, 99 maxwidth=self._width * 0.8, 100 scale=0.7, 101 h_align='center', 102 v_align='center', 103 ) 104 105 self._status_text = ba.textwidget( 106 parent=self._root_widget, 107 position=(self._width * 0.5, self._height - 160), 108 size=(0, 0), 109 text=ba.Lstr( 110 resource=self._r + '.checkingAvailabilityText', 111 subs=[('${NAME}', self._name)], 112 ), 113 color=(0.8, 0.4, 0.0), 114 maxwidth=self._width * 0.8, 115 scale=0.65, 116 h_align='center', 117 v_align='center', 118 ) 119 120 self._price_text = ba.textwidget( 121 parent=self._root_widget, 122 position=(self._width * 0.5, self._height - 230), 123 size=(0, 0), 124 text='', 125 color=(0.2, 1, 0.2), 126 maxwidth=self._width * 0.8, 127 scale=1.5, 128 h_align='center', 129 v_align='center', 130 ) 131 132 self._tickets_text: ba.Widget | None 133 if not ba.app.ui.use_toolbars: 134 self._tickets_text = ba.textwidget( 135 parent=self._root_widget, 136 position=(self._width * 0.9 - 5, self._height - 30), 137 size=(0, 0), 138 text=ba.charstr(ba.SpecialChar.TICKET) + '123', 139 color=(0.2, 1, 0.2), 140 maxwidth=100, 141 scale=0.5, 142 h_align='right', 143 v_align='center', 144 ) 145 else: 146 self._tickets_text = None 147 148 master_server_get( 149 'bsGlobalProfileCheck', 150 {'name': self._name, 'b': ba.app.build_number}, 151 callback=ba.WeakCall(self._profile_check_result), 152 ) 153 self._cost = ba.internal.get_v1_account_misc_read_val( 154 'price.global_profile', 500 155 ) 156 self._status: str | None = 'waiting' 157 self._update_timer = ba.Timer( 158 1.0, 159 ba.WeakCall(self._update), 160 timetype=ba.TimeType.REAL, 161 repeat=True, 162 ) 163 self._update()
Inherited Members
- ba.ui.Window
- get_root_widget