bastd.ui.coop.gamebutton

Defines button for co-op games.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Defines button for co-op games."""
  4
  5from __future__ import annotations
  6
  7import random
  8from typing import TYPE_CHECKING
  9
 10import ba
 11
 12if TYPE_CHECKING:
 13    from bastd.ui.coop.browser import CoopBrowserWindow
 14
 15
 16class GameButton:
 17    """Button for entering co-op games."""
 18
 19    def __init__(
 20        self,
 21        window: CoopBrowserWindow,
 22        parent: ba.Widget,
 23        game: str,
 24        x: float,
 25        y: float,
 26        select: bool,
 27        row: str,
 28    ):
 29        # pylint: disable=too-many-statements
 30        # pylint: disable=too-many-locals
 31        from ba.internal import getcampaign
 32
 33        self._game = game
 34        sclx = 195.0
 35        scly = 195.0
 36
 37        campaignname, levelname = game.split(':')
 38
 39        # Hack: The Last Stand doesn't actually exist in the easy
 40        # tourney. We just want it for display purposes. Map it to
 41        # the hard-mode version.
 42        if game == 'Easy:The Last Stand':
 43            campaignname = 'Default'
 44
 45        rating: float | None
 46        campaign = getcampaign(campaignname)
 47        rating = campaign.getlevel(levelname).rating
 48
 49        if game == 'Easy:The Last Stand':
 50            rating = None
 51
 52        if rating is None or rating == 0.0:
 53            stars = 0
 54        elif rating >= 9.5:
 55            stars = 3
 56        elif rating >= 7.5:
 57            stars = 2
 58        else:
 59            stars = 1
 60
 61        self._button = btn = ba.buttonwidget(
 62            parent=parent,
 63            position=(x + 23, y + 4),
 64            size=(sclx, scly),
 65            label='',
 66            on_activate_call=ba.Call(window.run_game, game),
 67            button_type='square',
 68            autoselect=True,
 69            on_select_call=ba.Call(window.sel_change, row, game),
 70        )
 71        ba.widget(
 72            edit=btn,
 73            show_buffer_bottom=50,
 74            show_buffer_top=50,
 75            show_buffer_left=400,
 76            show_buffer_right=200,
 77        )
 78        if select:
 79            ba.containerwidget(
 80                edit=parent, selected_child=btn, visible_child=btn
 81            )
 82        image_width = sclx * 0.85 * 0.75
 83        self._preview_widget = ba.imagewidget(
 84            parent=parent,
 85            draw_controller=btn,
 86            position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 104),
 87            size=(image_width, image_width * 0.5),
 88            model_transparent=window.lsbt,
 89            model_opaque=window.lsbo,
 90            texture=campaign.getlevel(levelname).get_preview_texture(),
 91            mask_texture=ba.gettexture('mapPreviewMask'),
 92        )
 93
 94        translated = campaign.getlevel(levelname).displayname
 95        self._achievements = ba.app.ach.achievements_for_coop_level(game)
 96
 97        self._name_widget = ba.textwidget(
 98            parent=parent,
 99            draw_controller=btn,
100            position=(x + 20 + sclx * 0.5, y + scly - 27),
101            size=(0, 0),
102            h_align='center',
103            text=translated,
104            v_align='center',
105            maxwidth=sclx * 0.76,
106            scale=0.85,
107        )
108        xscl = x + (67 if self._achievements else 50)
109        yscl = y + scly - (137 if self._achievements else 157)
110
111        starscale = 35.0 if self._achievements else 45.0
112
113        self._star_widgets: list[ba.Widget] = []
114        for _i in range(stars):
115            imw = ba.imagewidget(
116                parent=parent,
117                draw_controller=btn,
118                position=(xscl, yscl),
119                size=(starscale, starscale),
120                texture=window.star_tex,
121            )
122            self._star_widgets.append(imw)
123            xscl += starscale
124        for _i in range(3 - stars):
125            ba.imagewidget(
126                parent=parent,
127                draw_controller=btn,
128                position=(xscl, yscl),
129                size=(starscale, starscale),
130                color=(0, 0, 0),
131                texture=window.star_tex,
132                opacity=0.3,
133            )
134            xscl += starscale
135
136        xach = x + 69
137        yach = y + scly - 168
138        a_scale = 30.0
139        self._achievement_widgets: list[tuple[ba.Widget, ba.Widget]] = []
140        for ach in self._achievements:
141            a_complete = ach.complete
142            imw = ba.imagewidget(
143                parent=parent,
144                draw_controller=btn,
145                position=(xach, yach),
146                size=(a_scale, a_scale),
147                color=tuple(ach.get_icon_color(a_complete)[:3])
148                if a_complete
149                else (1.2, 1.2, 1.2),
150                texture=ach.get_icon_texture(a_complete),
151            )
152            imw2 = ba.imagewidget(
153                parent=parent,
154                draw_controller=btn,
155                position=(xach, yach),
156                size=(a_scale, a_scale),
157                color=(2, 1.4, 0.4),
158                texture=window.a_outline_tex,
159                model_transparent=window.a_outline_model,
160            )
161            self._achievement_widgets.append((imw, imw2))
162            # if a_complete:
163            xach += a_scale * 1.2
164
165        # if not unlocked:
166        self._lock_widget = ba.imagewidget(
167            parent=parent,
168            draw_controller=btn,
169            position=(x - 8 + sclx * 0.5, y + scly * 0.5 - 20),
170            size=(60, 60),
171            opacity=0.0,
172            texture=ba.gettexture('lock'),
173        )
174
175        # give a quasi-random update increment to spread the load..
176        self._update_timer = ba.Timer(
177            0.001 * (900 + random.randrange(200)),
178            ba.WeakCall(self._update),
179            repeat=True,
180            timetype=ba.TimeType.REAL,
181        )
182        self._update()
183
184    def get_button(self) -> ba.Widget:
185        """Return the underlying button ba.Widget."""
186        return self._button
187
188    def _update(self) -> None:
189        # pylint: disable=too-many-boolean-expressions
190        from ba.internal import getcampaign
191
192        # In case we stick around after our UI...
193        if not self._button:
194            return
195
196        game = self._game
197        campaignname, levelname = game.split(':')
198
199        # Hack - The Last Stand doesn't actually exist in the
200        # easy tourney; we just want it for display purposes. Map it to
201        # the hard-mode version.
202        if game == 'Easy:The Last Stand':
203            campaignname = 'Default'
204
205        campaign = getcampaign(campaignname)
206
207        # If this campaign is sequential, make sure we've unlocked
208        # everything up to here.
209        unlocked = True
210        if campaign.sequential:
211            for level in campaign.levels:
212                if level.name == levelname:
213                    break
214                if not level.complete:
215                    unlocked = False
216                    break
217
218        # We never actually allow playing last-stand on easy mode.
219        if game == 'Easy:The Last Stand':
220            unlocked = False
221
222        # Hard-code games we haven't unlocked.
223        if (
224            (
225                game
226                in (
227                    'Challenges:Infinite Runaround',
228                    'Challenges:Infinite Onslaught',
229                )
230                and not ba.app.accounts_v1.have_pro()
231            )
232            or (
233                game in ('Challenges:Meteor Shower',)
234                and not ba.internal.get_purchased('games.meteor_shower')
235            )
236            or (
237                game
238                in (
239                    'Challenges:Target Practice',
240                    'Challenges:Target Practice B',
241                )
242                and not ba.internal.get_purchased('games.target_practice')
243            )
244            or (
245                game in ('Challenges:Ninja Fight',)
246                and not ba.internal.get_purchased('games.ninja_fight')
247            )
248            or (
249                game in ('Challenges:Pro Ninja Fight',)
250                and not ba.internal.get_purchased('games.ninja_fight')
251            )
252            or (
253                game
254                in (
255                    'Challenges:Easter Egg Hunt',
256                    'Challenges:Pro Easter Egg Hunt',
257                )
258                and not ba.internal.get_purchased('games.easter_egg_hunt')
259            )
260        ):
261            unlocked = False
262
263        # Let's tint levels a slightly different color when easy mode
264        # is selected.
265        unlocked_color = (
266            (0.85, 0.95, 0.5) if game.startswith('Easy:') else (0.5, 0.7, 0.2)
267        )
268
269        ba.buttonwidget(
270            edit=self._button,
271            color=unlocked_color if unlocked else (0.5, 0.5, 0.5),
272        )
273
274        ba.imagewidget(edit=self._lock_widget, opacity=0.0 if unlocked else 1.0)
275        ba.imagewidget(
276            edit=self._preview_widget, opacity=1.0 if unlocked else 0.3
277        )
278        ba.textwidget(
279            edit=self._name_widget,
280            color=(0.8, 1.0, 0.8, 1.0) if unlocked else (0.7, 0.7, 0.7, 0.7),
281        )
282        for widget in self._star_widgets:
283            ba.imagewidget(
284                edit=widget,
285                opacity=1.0 if unlocked else 0.3,
286                color=(2.2, 1.2, 0.3) if unlocked else (1, 1, 1),
287            )
288        for i, ach in enumerate(self._achievements):
289            a_complete = ach.complete
290            ba.imagewidget(
291                edit=self._achievement_widgets[i][0],
292                opacity=1.0 if (a_complete and unlocked) else 0.3,
293            )
294            ba.imagewidget(
295                edit=self._achievement_widgets[i][1],
296                opacity=(
297                    1.0
298                    if (a_complete and unlocked)
299                    else 0.2
300                    if a_complete
301                    else 0.0
302                ),
303            )
class GameButton:
 17class GameButton:
 18    """Button for entering co-op games."""
 19
 20    def __init__(
 21        self,
 22        window: CoopBrowserWindow,
 23        parent: ba.Widget,
 24        game: str,
 25        x: float,
 26        y: float,
 27        select: bool,
 28        row: str,
 29    ):
 30        # pylint: disable=too-many-statements
 31        # pylint: disable=too-many-locals
 32        from ba.internal import getcampaign
 33
 34        self._game = game
 35        sclx = 195.0
 36        scly = 195.0
 37
 38        campaignname, levelname = game.split(':')
 39
 40        # Hack: The Last Stand doesn't actually exist in the easy
 41        # tourney. We just want it for display purposes. Map it to
 42        # the hard-mode version.
 43        if game == 'Easy:The Last Stand':
 44            campaignname = 'Default'
 45
 46        rating: float | None
 47        campaign = getcampaign(campaignname)
 48        rating = campaign.getlevel(levelname).rating
 49
 50        if game == 'Easy:The Last Stand':
 51            rating = None
 52
 53        if rating is None or rating == 0.0:
 54            stars = 0
 55        elif rating >= 9.5:
 56            stars = 3
 57        elif rating >= 7.5:
 58            stars = 2
 59        else:
 60            stars = 1
 61
 62        self._button = btn = ba.buttonwidget(
 63            parent=parent,
 64            position=(x + 23, y + 4),
 65            size=(sclx, scly),
 66            label='',
 67            on_activate_call=ba.Call(window.run_game, game),
 68            button_type='square',
 69            autoselect=True,
 70            on_select_call=ba.Call(window.sel_change, row, game),
 71        )
 72        ba.widget(
 73            edit=btn,
 74            show_buffer_bottom=50,
 75            show_buffer_top=50,
 76            show_buffer_left=400,
 77            show_buffer_right=200,
 78        )
 79        if select:
 80            ba.containerwidget(
 81                edit=parent, selected_child=btn, visible_child=btn
 82            )
 83        image_width = sclx * 0.85 * 0.75
 84        self._preview_widget = ba.imagewidget(
 85            parent=parent,
 86            draw_controller=btn,
 87            position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 104),
 88            size=(image_width, image_width * 0.5),
 89            model_transparent=window.lsbt,
 90            model_opaque=window.lsbo,
 91            texture=campaign.getlevel(levelname).get_preview_texture(),
 92            mask_texture=ba.gettexture('mapPreviewMask'),
 93        )
 94
 95        translated = campaign.getlevel(levelname).displayname
 96        self._achievements = ba.app.ach.achievements_for_coop_level(game)
 97
 98        self._name_widget = ba.textwidget(
 99            parent=parent,
100            draw_controller=btn,
101            position=(x + 20 + sclx * 0.5, y + scly - 27),
102            size=(0, 0),
103            h_align='center',
104            text=translated,
105            v_align='center',
106            maxwidth=sclx * 0.76,
107            scale=0.85,
108        )
109        xscl = x + (67 if self._achievements else 50)
110        yscl = y + scly - (137 if self._achievements else 157)
111
112        starscale = 35.0 if self._achievements else 45.0
113
114        self._star_widgets: list[ba.Widget] = []
115        for _i in range(stars):
116            imw = ba.imagewidget(
117                parent=parent,
118                draw_controller=btn,
119                position=(xscl, yscl),
120                size=(starscale, starscale),
121                texture=window.star_tex,
122            )
123            self._star_widgets.append(imw)
124            xscl += starscale
125        for _i in range(3 - stars):
126            ba.imagewidget(
127                parent=parent,
128                draw_controller=btn,
129                position=(xscl, yscl),
130                size=(starscale, starscale),
131                color=(0, 0, 0),
132                texture=window.star_tex,
133                opacity=0.3,
134            )
135            xscl += starscale
136
137        xach = x + 69
138        yach = y + scly - 168
139        a_scale = 30.0
140        self._achievement_widgets: list[tuple[ba.Widget, ba.Widget]] = []
141        for ach in self._achievements:
142            a_complete = ach.complete
143            imw = ba.imagewidget(
144                parent=parent,
145                draw_controller=btn,
146                position=(xach, yach),
147                size=(a_scale, a_scale),
148                color=tuple(ach.get_icon_color(a_complete)[:3])
149                if a_complete
150                else (1.2, 1.2, 1.2),
151                texture=ach.get_icon_texture(a_complete),
152            )
153            imw2 = ba.imagewidget(
154                parent=parent,
155                draw_controller=btn,
156                position=(xach, yach),
157                size=(a_scale, a_scale),
158                color=(2, 1.4, 0.4),
159                texture=window.a_outline_tex,
160                model_transparent=window.a_outline_model,
161            )
162            self._achievement_widgets.append((imw, imw2))
163            # if a_complete:
164            xach += a_scale * 1.2
165
166        # if not unlocked:
167        self._lock_widget = ba.imagewidget(
168            parent=parent,
169            draw_controller=btn,
170            position=(x - 8 + sclx * 0.5, y + scly * 0.5 - 20),
171            size=(60, 60),
172            opacity=0.0,
173            texture=ba.gettexture('lock'),
174        )
175
176        # give a quasi-random update increment to spread the load..
177        self._update_timer = ba.Timer(
178            0.001 * (900 + random.randrange(200)),
179            ba.WeakCall(self._update),
180            repeat=True,
181            timetype=ba.TimeType.REAL,
182        )
183        self._update()
184
185    def get_button(self) -> ba.Widget:
186        """Return the underlying button ba.Widget."""
187        return self._button
188
189    def _update(self) -> None:
190        # pylint: disable=too-many-boolean-expressions
191        from ba.internal import getcampaign
192
193        # In case we stick around after our UI...
194        if not self._button:
195            return
196
197        game = self._game
198        campaignname, levelname = game.split(':')
199
200        # Hack - The Last Stand doesn't actually exist in the
201        # easy tourney; we just want it for display purposes. Map it to
202        # the hard-mode version.
203        if game == 'Easy:The Last Stand':
204            campaignname = 'Default'
205
206        campaign = getcampaign(campaignname)
207
208        # If this campaign is sequential, make sure we've unlocked
209        # everything up to here.
210        unlocked = True
211        if campaign.sequential:
212            for level in campaign.levels:
213                if level.name == levelname:
214                    break
215                if not level.complete:
216                    unlocked = False
217                    break
218
219        # We never actually allow playing last-stand on easy mode.
220        if game == 'Easy:The Last Stand':
221            unlocked = False
222
223        # Hard-code games we haven't unlocked.
224        if (
225            (
226                game
227                in (
228                    'Challenges:Infinite Runaround',
229                    'Challenges:Infinite Onslaught',
230                )
231                and not ba.app.accounts_v1.have_pro()
232            )
233            or (
234                game in ('Challenges:Meteor Shower',)
235                and not ba.internal.get_purchased('games.meteor_shower')
236            )
237            or (
238                game
239                in (
240                    'Challenges:Target Practice',
241                    'Challenges:Target Practice B',
242                )
243                and not ba.internal.get_purchased('games.target_practice')
244            )
245            or (
246                game in ('Challenges:Ninja Fight',)
247                and not ba.internal.get_purchased('games.ninja_fight')
248            )
249            or (
250                game in ('Challenges:Pro Ninja Fight',)
251                and not ba.internal.get_purchased('games.ninja_fight')
252            )
253            or (
254                game
255                in (
256                    'Challenges:Easter Egg Hunt',
257                    'Challenges:Pro Easter Egg Hunt',
258                )
259                and not ba.internal.get_purchased('games.easter_egg_hunt')
260            )
261        ):
262            unlocked = False
263
264        # Let's tint levels a slightly different color when easy mode
265        # is selected.
266        unlocked_color = (
267            (0.85, 0.95, 0.5) if game.startswith('Easy:') else (0.5, 0.7, 0.2)
268        )
269
270        ba.buttonwidget(
271            edit=self._button,
272            color=unlocked_color if unlocked else (0.5, 0.5, 0.5),
273        )
274
275        ba.imagewidget(edit=self._lock_widget, opacity=0.0 if unlocked else 1.0)
276        ba.imagewidget(
277            edit=self._preview_widget, opacity=1.0 if unlocked else 0.3
278        )
279        ba.textwidget(
280            edit=self._name_widget,
281            color=(0.8, 1.0, 0.8, 1.0) if unlocked else (0.7, 0.7, 0.7, 0.7),
282        )
283        for widget in self._star_widgets:
284            ba.imagewidget(
285                edit=widget,
286                opacity=1.0 if unlocked else 0.3,
287                color=(2.2, 1.2, 0.3) if unlocked else (1, 1, 1),
288            )
289        for i, ach in enumerate(self._achievements):
290            a_complete = ach.complete
291            ba.imagewidget(
292                edit=self._achievement_widgets[i][0],
293                opacity=1.0 if (a_complete and unlocked) else 0.3,
294            )
295            ba.imagewidget(
296                edit=self._achievement_widgets[i][1],
297                opacity=(
298                    1.0
299                    if (a_complete and unlocked)
300                    else 0.2
301                    if a_complete
302                    else 0.0
303                ),
304            )

Button for entering co-op games.

GameButton( window: bastd.ui.coop.browser.CoopBrowserWindow, parent: _ba.Widget, game: str, x: float, y: float, select: bool, row: str)
 20    def __init__(
 21        self,
 22        window: CoopBrowserWindow,
 23        parent: ba.Widget,
 24        game: str,
 25        x: float,
 26        y: float,
 27        select: bool,
 28        row: str,
 29    ):
 30        # pylint: disable=too-many-statements
 31        # pylint: disable=too-many-locals
 32        from ba.internal import getcampaign
 33
 34        self._game = game
 35        sclx = 195.0
 36        scly = 195.0
 37
 38        campaignname, levelname = game.split(':')
 39
 40        # Hack: The Last Stand doesn't actually exist in the easy
 41        # tourney. We just want it for display purposes. Map it to
 42        # the hard-mode version.
 43        if game == 'Easy:The Last Stand':
 44            campaignname = 'Default'
 45
 46        rating: float | None
 47        campaign = getcampaign(campaignname)
 48        rating = campaign.getlevel(levelname).rating
 49
 50        if game == 'Easy:The Last Stand':
 51            rating = None
 52
 53        if rating is None or rating == 0.0:
 54            stars = 0
 55        elif rating >= 9.5:
 56            stars = 3
 57        elif rating >= 7.5:
 58            stars = 2
 59        else:
 60            stars = 1
 61
 62        self._button = btn = ba.buttonwidget(
 63            parent=parent,
 64            position=(x + 23, y + 4),
 65            size=(sclx, scly),
 66            label='',
 67            on_activate_call=ba.Call(window.run_game, game),
 68            button_type='square',
 69            autoselect=True,
 70            on_select_call=ba.Call(window.sel_change, row, game),
 71        )
 72        ba.widget(
 73            edit=btn,
 74            show_buffer_bottom=50,
 75            show_buffer_top=50,
 76            show_buffer_left=400,
 77            show_buffer_right=200,
 78        )
 79        if select:
 80            ba.containerwidget(
 81                edit=parent, selected_child=btn, visible_child=btn
 82            )
 83        image_width = sclx * 0.85 * 0.75
 84        self._preview_widget = ba.imagewidget(
 85            parent=parent,
 86            draw_controller=btn,
 87            position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 104),
 88            size=(image_width, image_width * 0.5),
 89            model_transparent=window.lsbt,
 90            model_opaque=window.lsbo,
 91            texture=campaign.getlevel(levelname).get_preview_texture(),
 92            mask_texture=ba.gettexture('mapPreviewMask'),
 93        )
 94
 95        translated = campaign.getlevel(levelname).displayname
 96        self._achievements = ba.app.ach.achievements_for_coop_level(game)
 97
 98        self._name_widget = ba.textwidget(
 99            parent=parent,
100            draw_controller=btn,
101            position=(x + 20 + sclx * 0.5, y + scly - 27),
102            size=(0, 0),
103            h_align='center',
104            text=translated,
105            v_align='center',
106            maxwidth=sclx * 0.76,
107            scale=0.85,
108        )
109        xscl = x + (67 if self._achievements else 50)
110        yscl = y + scly - (137 if self._achievements else 157)
111
112        starscale = 35.0 if self._achievements else 45.0
113
114        self._star_widgets: list[ba.Widget] = []
115        for _i in range(stars):
116            imw = ba.imagewidget(
117                parent=parent,
118                draw_controller=btn,
119                position=(xscl, yscl),
120                size=(starscale, starscale),
121                texture=window.star_tex,
122            )
123            self._star_widgets.append(imw)
124            xscl += starscale
125        for _i in range(3 - stars):
126            ba.imagewidget(
127                parent=parent,
128                draw_controller=btn,
129                position=(xscl, yscl),
130                size=(starscale, starscale),
131                color=(0, 0, 0),
132                texture=window.star_tex,
133                opacity=0.3,
134            )
135            xscl += starscale
136
137        xach = x + 69
138        yach = y + scly - 168
139        a_scale = 30.0
140        self._achievement_widgets: list[tuple[ba.Widget, ba.Widget]] = []
141        for ach in self._achievements:
142            a_complete = ach.complete
143            imw = ba.imagewidget(
144                parent=parent,
145                draw_controller=btn,
146                position=(xach, yach),
147                size=(a_scale, a_scale),
148                color=tuple(ach.get_icon_color(a_complete)[:3])
149                if a_complete
150                else (1.2, 1.2, 1.2),
151                texture=ach.get_icon_texture(a_complete),
152            )
153            imw2 = ba.imagewidget(
154                parent=parent,
155                draw_controller=btn,
156                position=(xach, yach),
157                size=(a_scale, a_scale),
158                color=(2, 1.4, 0.4),
159                texture=window.a_outline_tex,
160                model_transparent=window.a_outline_model,
161            )
162            self._achievement_widgets.append((imw, imw2))
163            # if a_complete:
164            xach += a_scale * 1.2
165
166        # if not unlocked:
167        self._lock_widget = ba.imagewidget(
168            parent=parent,
169            draw_controller=btn,
170            position=(x - 8 + sclx * 0.5, y + scly * 0.5 - 20),
171            size=(60, 60),
172            opacity=0.0,
173            texture=ba.gettexture('lock'),
174        )
175
176        # give a quasi-random update increment to spread the load..
177        self._update_timer = ba.Timer(
178            0.001 * (900 + random.randrange(200)),
179            ba.WeakCall(self._update),
180            repeat=True,
181            timetype=ba.TimeType.REAL,
182        )
183        self._update()
def get_button(self) -> _ba.Widget:
185    def get_button(self) -> ba.Widget:
186        """Return the underlying button ba.Widget."""
187        return self._button

Return the underlying button ba.Widget.