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