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                print('FIXME - show not-enough-tickets msg.')
209                # gettickets.show_get_tickets_prompt()
210                return
211            bui.screenmessage(
212                bui.Lstr(resource='purchasingText'), color=(0, 1, 0)
213            )
214            self._status = 'pre_upgrading'
215
216            # Now we tell the original editor to save the profile, add an
217            # upgrade transaction, and then sit and wait for everything to
218            # go through.
219            edit_profile_window = self._edit_profile_window()
220            if edit_profile_window is None:
221                print('profile upgrade: original edit window gone')
222                return
223            success = edit_profile_window.save(transition_out=False)
224            if not success:
225                print('profile upgrade: error occurred saving profile')
226                bui.screenmessage(
227                    bui.Lstr(resource='errorText'), color=(1, 0, 0)
228                )
229                bui.getsound('error').play()
230                return
231            plus.add_v1_account_transaction(
232                {'type': 'UPGRADE_PROFILE', 'name': self._name}
233            )
234            plus.run_v1_account_transactions()
235            self._status = 'upgrading'
236            self._upgrade_start_time = time.time()
237        else:
238            bui.getsound('error').play()
239
240    def _update(self) -> None:
241        plus = bui.app.plus
242        assert plus is not None
243
244        # If our originating window dies at any point, cancel.
245        edit_profile_window = self._edit_profile_window()
246        if edit_profile_window is None:
247            self._cancel()
248            return
249
250        # Once we've kicked off an upgrade attempt and all transactions go
251        # through, we're done.
252        if (
253            self._status == 'upgrading'
254            and not plus.have_outstanding_v1_account_transactions()
255        ):
256            self._status = 'exiting'
257            bui.containerwidget(edit=self._root_widget, transition='out_right')
258            edit_profile_window = self._edit_profile_window()
259            if edit_profile_window is None:
260                print(
261                    'profile upgrade transition out:'
262                    ' original edit window gone'
263                )
264                return
265            bui.getsound('gunCocking').play()
266            edit_profile_window.reload_window()
267
268    def _cancel(self) -> None:
269        # If we recently sent out an upgrade request, disallow canceling
270        # for a bit.
271        if (
272            self._upgrade_start_time is not None
273            and time.time() - self._upgrade_start_time < 10.0
274        ):
275            bui.getsound('error').play()
276            return
277        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                print('FIXME - show not-enough-tickets msg.')
210                # gettickets.show_get_tickets_prompt()
211                return
212            bui.screenmessage(
213                bui.Lstr(resource='purchasingText'), color=(0, 1, 0)
214            )
215            self._status = 'pre_upgrading'
216
217            # Now we tell the original editor to save the profile, add an
218            # upgrade transaction, and then sit and wait for everything to
219            # go through.
220            edit_profile_window = self._edit_profile_window()
221            if edit_profile_window is None:
222                print('profile upgrade: original edit window gone')
223                return
224            success = edit_profile_window.save(transition_out=False)
225            if not success:
226                print('profile upgrade: error occurred saving profile')
227                bui.screenmessage(
228                    bui.Lstr(resource='errorText'), color=(1, 0, 0)
229                )
230                bui.getsound('error').play()
231                return
232            plus.add_v1_account_transaction(
233                {'type': 'UPGRADE_PROFILE', 'name': self._name}
234            )
235            plus.run_v1_account_transactions()
236            self._status = 'upgrading'
237            self._upgrade_start_time = time.time()
238        else:
239            bui.getsound('error').play()
240
241    def _update(self) -> None:
242        plus = bui.app.plus
243        assert plus is not None
244
245        # If our originating window dies at any point, cancel.
246        edit_profile_window = self._edit_profile_window()
247        if edit_profile_window is None:
248            self._cancel()
249            return
250
251        # Once we've kicked off an upgrade attempt and all transactions go
252        # through, we're done.
253        if (
254            self._status == 'upgrading'
255            and not plus.have_outstanding_v1_account_transactions()
256        ):
257            self._status = 'exiting'
258            bui.containerwidget(edit=self._root_widget, transition='out_right')
259            edit_profile_window = self._edit_profile_window()
260            if edit_profile_window is None:
261                print(
262                    'profile upgrade transition out:'
263                    ' original edit window gone'
264                )
265                return
266            bui.getsound('gunCocking').play()
267            edit_profile_window.reload_window()
268
269    def _cancel(self) -> None:
270        # If we recently sent out an upgrade request, disallow canceling
271        # for a bit.
272        if (
273            self._upgrade_start_time is not None
274            and time.time() - self._upgrade_start_time < 10.0
275        ):
276            bui.getsound('error').play()
277            return
278        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()