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