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