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