bastd.ui.store.item

UI functionality related to UI items.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""UI functionality related to UI items."""
  4from __future__ import annotations
  5
  6from typing import TYPE_CHECKING
  7
  8import ba
  9
 10if TYPE_CHECKING:
 11    from typing import Any
 12
 13
 14def instantiate_store_item_display(
 15    item_name: str,
 16    item: dict[str, Any],
 17    parent_widget: ba.Widget,
 18    b_pos: tuple[float, float],
 19    b_width: float,
 20    b_height: float,
 21    boffs_h: float = 0.0,
 22    boffs_h2: float = 0.0,
 23    boffs_v2: float = 0,
 24    delay: float = 0.0,
 25    button: bool = True,
 26) -> None:
 27    """(internal)"""
 28    # pylint: disable=too-many-statements
 29    # pylint: disable=too-many-branches
 30    # pylint: disable=too-many-locals
 31    from ba.internal import (
 32        get_store_item,
 33        get_store_item_name_translated,
 34        get_clean_price,
 35    )
 36
 37    del boffs_h  # unused arg
 38    del boffs_h2  # unused arg
 39    del boffs_v2  # unused arg
 40    item_info = get_store_item(item_name)
 41    title_v = 0.24
 42    price_v = 0.145
 43    base_text_scale = 1.0
 44
 45    item['name'] = title = get_store_item_name_translated(item_name)
 46
 47    btn: ba.Widget | None
 48
 49    # Hack; showbuffer stuff isn't working well when we're showing merch.
 50    showbuffer = 10 if item_name in {'merch', 'pro', 'pro_sale'} else 76.0
 51
 52    if button:
 53        item['button'] = btn = ba.buttonwidget(
 54            parent=parent_widget,
 55            position=b_pos,
 56            transition_delay=delay,
 57            show_buffer_top=showbuffer,
 58            enable_sound=False,
 59            button_type='square',
 60            size=(b_width, b_height),
 61            autoselect=True,
 62            label='',
 63        )
 64        ba.widget(edit=btn, show_buffer_bottom=showbuffer)
 65    else:
 66        btn = None
 67
 68    b_offs_x = -0.015 * b_width
 69    check_pos = 0.76
 70
 71    icon_tex = None
 72    tint_tex = None
 73    tint_color = None
 74    tint2_color = None
 75    tex_name: str | None = None
 76    desc: str | None = None
 77    modes: ba.Lstr | None = None
 78
 79    if item_name.startswith('characters.'):
 80        character = ba.app.spaz_appearances[item_info['character']]
 81        tint_color = (
 82            item_info['color']
 83            if 'color' in item_info
 84            else character.default_color
 85            if character.default_color is not None
 86            else (1, 1, 1)
 87        )
 88        tint2_color = (
 89            item_info['highlight']
 90            if 'highlight' in item_info
 91            else character.default_highlight
 92            if character.default_highlight is not None
 93            else (1, 1, 1)
 94        )
 95        icon_tex = character.icon_texture
 96        tint_tex = character.icon_mask_texture
 97        title_v = 0.255
 98        price_v = 0.145
 99    elif item_name == 'merch':
100        base_text_scale = 0.6
101        title_v = 0.85
102        price_v = 0.15
103    elif item_name in ['upgrades.pro', 'pro']:
104        base_text_scale = 0.6
105        title_v = 0.85
106        price_v = 0.15
107    elif item_name.startswith('maps.'):
108        map_type = item_info['map_type']
109        tex_name = map_type.get_preview_texture_name()
110        title_v = 0.312
111        price_v = 0.17
112
113    elif item_name.startswith('games.'):
114        gametype = item_info['gametype']
115        modes_l = []
116        if gametype.supports_session_type(ba.CoopSession):
117            modes_l.append(ba.Lstr(resource='playModes.coopText'))
118        if gametype.supports_session_type(ba.DualTeamSession):
119            modes_l.append(ba.Lstr(resource='playModes.teamsText'))
120        if gametype.supports_session_type(ba.FreeForAllSession):
121            modes_l.append(ba.Lstr(resource='playModes.freeForAllText'))
122
123        if len(modes_l) == 3:
124            modes = ba.Lstr(
125                value='${A}, ${B}, ${C}',
126                subs=[
127                    ('${A}', modes_l[0]),
128                    ('${B}', modes_l[1]),
129                    ('${C}', modes_l[2]),
130                ],
131            )
132        elif len(modes_l) == 2:
133            modes = ba.Lstr(
134                value='${A}, ${B}',
135                subs=[('${A}', modes_l[0]), ('${B}', modes_l[1])],
136            )
137        elif len(modes_l) == 1:
138            modes = modes_l[0]
139        else:
140            raise Exception()
141        desc = gametype.get_description_display_string(ba.CoopSession)
142        tex_name = item_info['previewTex']
143        base_text_scale = 0.8
144        title_v = 0.48
145        price_v = 0.17
146
147    elif item_name.startswith('icons.'):
148        base_text_scale = 1.5
149        price_v = 0.2
150        check_pos = 0.6
151
152    if item_name.startswith('characters.'):
153        frame_size = b_width * 0.7
154        im_dim = frame_size * (100.0 / 113.0)
155        im_pos = (
156            b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x,
157            b_pos[1] + b_height * 0.57 - im_dim * 0.5,
158        )
159        mask_texture = ba.gettexture('characterIconMask')
160        assert icon_tex is not None
161        assert tint_tex is not None
162        ba.imagewidget(
163            parent=parent_widget,
164            position=im_pos,
165            size=(im_dim, im_dim),
166            color=(1, 1, 1),
167            transition_delay=delay,
168            mask_texture=mask_texture,
169            draw_controller=btn,
170            texture=ba.gettexture(icon_tex),
171            tint_texture=ba.gettexture(tint_tex),
172            tint_color=tint_color,
173            tint2_color=tint2_color,
174        )
175
176    if item_name == 'merch':
177        frame_size = b_width * 0.65
178        im_dim = frame_size * (100.0 / 113.0)
179        im_pos = (
180            b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x,
181            b_pos[1] + b_height * 0.47 - im_dim * 0.5,
182        )
183        ba.imagewidget(
184            parent=parent_widget,
185            position=im_pos,
186            size=(im_dim, im_dim),
187            transition_delay=delay,
188            draw_controller=btn,
189            opacity=1.0,
190            texture=ba.gettexture('merch'),
191        )
192
193    if item_name in ['pro', 'upgrades.pro']:
194        frame_size = b_width * 0.5
195        im_dim = frame_size * (100.0 / 113.0)
196        im_pos = (
197            b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x,
198            b_pos[1] + b_height * 0.5 - im_dim * 0.5,
199        )
200        ba.imagewidget(
201            parent=parent_widget,
202            position=im_pos,
203            size=(im_dim, im_dim),
204            transition_delay=delay,
205            draw_controller=btn,
206            color=(0.3, 0.0, 0.3),
207            opacity=0.3,
208            texture=ba.gettexture('logo'),
209        )
210        txt = ba.Lstr(resource='store.bombSquadProNewDescriptionText')
211
212        item['descriptionText'] = ba.textwidget(
213            parent=parent_widget,
214            text=txt,
215            position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.69),
216            transition_delay=delay,
217            scale=b_width * (1.0 / 230.0) * base_text_scale * 0.75,
218            maxwidth=b_width * 0.75,
219            max_height=b_height * 0.2,
220            size=(0, 0),
221            h_align='center',
222            v_align='center',
223            draw_controller=btn,
224            color=(0.3, 1, 0.3),
225        )
226
227        extra_backings = item['extra_backings'] = []
228        extra_images = item['extra_images'] = []
229        extra_texts = item['extra_texts'] = []
230        extra_texts_2 = item['extra_texts_2'] = []
231
232        backing_color = (0.5, 0.8, 0.3) if button else (0.6, 0.5, 0.65)
233        b_square_texture = ba.gettexture('buttonSquare')
234        char_mask_texture = ba.gettexture('characterIconMask')
235
236        pos = (0.17, 0.43)
237        tile_size = (b_width * 0.16 * 1.2, b_width * 0.2 * 1.2)
238        tile_pos = (b_pos[0] + b_width * pos[0], b_pos[1] + b_height * pos[1])
239        extra_backings.append(
240            ba.imagewidget(
241                parent=parent_widget,
242                position=(
243                    tile_pos[0] - tile_size[0] * 0.5,
244                    tile_pos[1] - tile_size[1] * 0.5,
245                ),
246                size=tile_size,
247                transition_delay=delay,
248                draw_controller=btn,
249                color=backing_color,
250                texture=b_square_texture,
251            )
252        )
253        im_size = tile_size[0] * 0.8
254        extra_images.append(
255            ba.imagewidget(
256                parent=parent_widget,
257                position=(
258                    tile_pos[0] - im_size * 0.5,
259                    tile_pos[1] - im_size * 0.4,
260                ),
261                size=(im_size, im_size),
262                transition_delay=delay,
263                draw_controller=btn,
264                color=(1, 1, 1),
265                texture=ba.gettexture('ticketsMore'),
266            )
267        )
268        bonus_tickets = str(
269            ba.internal.get_v1_account_misc_read_val('proBonusTickets', 100)
270        )
271        extra_texts.append(
272            ba.textwidget(
273                parent=parent_widget,
274                draw_controller=btn,
275                position=(
276                    tile_pos[0] - tile_size[0] * 0.03,
277                    tile_pos[1] - tile_size[1] * 0.25,
278                ),
279                size=(0, 0),
280                color=(0.6, 1, 0.6),
281                transition_delay=delay,
282                h_align='center',
283                v_align='center',
284                maxwidth=tile_size[0] * 0.7,
285                scale=0.55,
286                text=ba.Lstr(
287                    resource='getTicketsWindow.ticketsText',
288                    subs=[('${COUNT}', bonus_tickets)],
289                ),
290                flatness=1.0,
291                shadow=0.0,
292            )
293        )
294
295        for charname, pos in [
296            ('Kronk', (0.32, 0.45)),
297            ('Zoe', (0.425, 0.4)),
298            ('Jack Morgan', (0.555, 0.45)),
299            ('Mel', (0.645, 0.4)),
300        ]:
301            tile_size = (b_width * 0.16 * 0.9, b_width * 0.2 * 0.9)
302            tile_pos = (
303                b_pos[0] + b_width * pos[0],
304                b_pos[1] + b_height * pos[1],
305            )
306            character = ba.app.spaz_appearances[charname]
307            extra_backings.append(
308                ba.imagewidget(
309                    parent=parent_widget,
310                    position=(
311                        tile_pos[0] - tile_size[0] * 0.5,
312                        tile_pos[1] - tile_size[1] * 0.5,
313                    ),
314                    size=tile_size,
315                    transition_delay=delay,
316                    draw_controller=btn,
317                    color=backing_color,
318                    texture=b_square_texture,
319                )
320            )
321            im_size = tile_size[0] * 0.7
322            extra_images.append(
323                ba.imagewidget(
324                    parent=parent_widget,
325                    position=(
326                        tile_pos[0] - im_size * 0.53,
327                        tile_pos[1] - im_size * 0.35,
328                    ),
329                    size=(im_size, im_size),
330                    transition_delay=delay,
331                    draw_controller=btn,
332                    color=(1, 1, 1),
333                    texture=ba.gettexture(character.icon_texture),
334                    tint_texture=ba.gettexture(character.icon_mask_texture),
335                    tint_color=character.default_color,
336                    tint2_color=character.default_highlight,
337                    mask_texture=char_mask_texture,
338                )
339            )
340            extra_texts.append(
341                ba.textwidget(
342                    parent=parent_widget,
343                    draw_controller=btn,
344                    position=(
345                        tile_pos[0] - im_size * 0.03,
346                        tile_pos[1] - im_size * 0.51,
347                    ),
348                    size=(0, 0),
349                    color=(0.6, 1, 0.6),
350                    transition_delay=delay,
351                    h_align='center',
352                    v_align='center',
353                    maxwidth=tile_size[0] * 0.7,
354                    scale=0.55,
355                    text=ba.Lstr(translate=('characterNames', charname)),
356                    flatness=1.0,
357                    shadow=0.0,
358                )
359            )
360
361        # If we have a 'total-worth' item-id for this id, show that price so
362        # the user knows how much this is worth.
363        total_worth_item = ba.internal.get_v1_account_misc_read_val(
364            'twrths', {}
365        ).get(item_name)
366        total_worth_price: str | None
367        if total_worth_item is not None:
368            price = ba.internal.get_price(total_worth_item)
369            total_worth_price = (
370                get_clean_price(price) if price is not None else '??'
371            )
372        else:
373            total_worth_price = None
374
375        if total_worth_price is not None:
376            total_worth_text = ba.Lstr(
377                resource='store.totalWorthText',
378                subs=[('${TOTAL_WORTH}', total_worth_price)],
379            )
380            extra_texts_2.append(
381                ba.textwidget(
382                    parent=parent_widget,
383                    text=total_worth_text,
384                    position=(
385                        b_pos[0] + b_width * 0.5 + b_offs_x,
386                        b_pos[1] + b_height * 0.25,
387                    ),
388                    transition_delay=delay,
389                    scale=b_width * (1.0 / 230.0) * base_text_scale * 0.45,
390                    maxwidth=b_width * 0.5,
391                    size=(0, 0),
392                    h_align='center',
393                    v_align='center',
394                    shadow=1.0,
395                    flatness=1.0,
396                    draw_controller=btn,
397                    color=(0.3, 1, 1),
398                )
399            )
400
401        model_opaque = ba.getmodel('level_select_button_opaque')
402        model_transparent = ba.getmodel('level_select_button_transparent')
403        mask_tex = ba.gettexture('mapPreviewMask')
404        for levelname, preview_tex_name, pos in [
405            ('Infinite Onslaught', 'doomShroomPreview', (0.80, 0.48)),
406            ('Infinite Runaround', 'towerDPreview', (0.80, 0.32)),
407        ]:
408            tile_size = (b_width * 0.2, b_width * 0.13)
409            tile_pos = (
410                b_pos[0] + b_width * pos[0],
411                b_pos[1] + b_height * pos[1],
412            )
413            im_size = tile_size[0] * 0.8
414            extra_backings.append(
415                ba.imagewidget(
416                    parent=parent_widget,
417                    position=(
418                        tile_pos[0] - tile_size[0] * 0.5,
419                        tile_pos[1] - tile_size[1] * 0.5,
420                    ),
421                    size=tile_size,
422                    transition_delay=delay,
423                    draw_controller=btn,
424                    color=backing_color,
425                    texture=b_square_texture,
426                )
427            )
428
429            # Hack - gotta draw two transparent versions to avoid z issues.
430            for mod in model_opaque, model_transparent:
431                extra_images.append(
432                    ba.imagewidget(
433                        parent=parent_widget,
434                        position=(
435                            tile_pos[0] - im_size * 0.52,
436                            tile_pos[1] - im_size * 0.2,
437                        ),
438                        size=(im_size, im_size * 0.5),
439                        transition_delay=delay,
440                        model_transparent=mod,
441                        mask_texture=mask_tex,
442                        draw_controller=btn,
443                        texture=ba.gettexture(preview_tex_name),
444                    )
445                )
446
447            extra_texts.append(
448                ba.textwidget(
449                    parent=parent_widget,
450                    draw_controller=btn,
451                    position=(
452                        tile_pos[0] - im_size * 0.03,
453                        tile_pos[1] - im_size * 0.2,
454                    ),
455                    size=(0, 0),
456                    color=(0.6, 1, 0.6),
457                    transition_delay=delay,
458                    h_align='center',
459                    v_align='center',
460                    maxwidth=tile_size[0] * 0.7,
461                    scale=0.55,
462                    text=ba.Lstr(translate=('coopLevelNames', levelname)),
463                    flatness=1.0,
464                    shadow=0.0,
465                )
466            )
467
468    if item_name.startswith('icons.'):
469        item['icon_text'] = ba.textwidget(
470            parent=parent_widget,
471            text=item_info['icon'],
472            position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.5),
473            transition_delay=delay,
474            scale=b_width * (1.0 / 230.0) * base_text_scale * 2.0,
475            maxwidth=b_width * 0.9,
476            max_height=b_height * 0.9,
477            size=(0, 0),
478            h_align='center',
479            v_align='center',
480            draw_controller=btn,
481        )
482
483    if item_name.startswith('maps.'):
484        frame_size = b_width * 0.9
485        im_dim = frame_size * (100.0 / 113.0)
486        im_pos = (
487            b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x,
488            b_pos[1] + b_height * 0.62 - im_dim * 0.25,
489        )
490        model_opaque = ba.getmodel('level_select_button_opaque')
491        model_transparent = ba.getmodel('level_select_button_transparent')
492        mask_tex = ba.gettexture('mapPreviewMask')
493        assert tex_name is not None
494        ba.imagewidget(
495            parent=parent_widget,
496            position=im_pos,
497            size=(im_dim, im_dim * 0.5),
498            transition_delay=delay,
499            model_opaque=model_opaque,
500            model_transparent=model_transparent,
501            mask_texture=mask_tex,
502            draw_controller=btn,
503            texture=ba.gettexture(tex_name),
504        )
505
506    if item_name.startswith('games.'):
507        frame_size = b_width * 0.8
508        im_dim = frame_size * (100.0 / 113.0)
509        im_pos = (
510            b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x,
511            b_pos[1] + b_height * 0.72 - im_dim * 0.25,
512        )
513        model_opaque = ba.getmodel('level_select_button_opaque')
514        model_transparent = ba.getmodel('level_select_button_transparent')
515        mask_tex = ba.gettexture('mapPreviewMask')
516        assert tex_name is not None
517        ba.imagewidget(
518            parent=parent_widget,
519            position=im_pos,
520            size=(im_dim, im_dim * 0.5),
521            transition_delay=delay,
522            model_opaque=model_opaque,
523            model_transparent=model_transparent,
524            mask_texture=mask_tex,
525            draw_controller=btn,
526            texture=ba.gettexture(tex_name),
527        )
528        item['descriptionText'] = ba.textwidget(
529            parent=parent_widget,
530            text=desc,
531            position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.36),
532            transition_delay=delay,
533            scale=b_width * (1.0 / 230.0) * base_text_scale * 0.78,
534            maxwidth=b_width * 0.8,
535            max_height=b_height * 0.14,
536            size=(0, 0),
537            h_align='center',
538            v_align='center',
539            draw_controller=btn,
540            flatness=1.0,
541            shadow=0.0,
542            color=(0.6, 1, 0.6),
543        )
544        item['gameModesText'] = ba.textwidget(
545            parent=parent_widget,
546            text=modes,
547            position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.26),
548            transition_delay=delay,
549            scale=b_width * (1.0 / 230.0) * base_text_scale * 0.65,
550            maxwidth=b_width * 0.8,
551            size=(0, 0),
552            h_align='center',
553            v_align='center',
554            draw_controller=btn,
555            shadow=0,
556            flatness=1.0,
557            color=(0.6, 0.8, 0.6),
558        )
559
560    if not item_name.startswith('icons.'):
561        item['title_text'] = ba.textwidget(
562            parent=parent_widget,
563            text=title,
564            position=(
565                b_pos[0] + b_width * 0.5 + b_offs_x,
566                b_pos[1] + b_height * title_v,
567            ),
568            transition_delay=delay,
569            scale=b_width * (1.0 / 230.0) * base_text_scale,
570            maxwidth=b_width * 0.8,
571            size=(0, 0),
572            h_align='center',
573            v_align='center',
574            draw_controller=btn,
575            color=(0.7, 0.9, 0.7, 1.0),
576        )
577
578    item['purchase_check'] = ba.imagewidget(
579        parent=parent_widget,
580        position=(b_pos[0] + b_width * check_pos, b_pos[1] + b_height * 0.05),
581        transition_delay=delay,
582        model_transparent=ba.getmodel('checkTransparent'),
583        opacity=0.0,
584        size=(60, 60),
585        color=(0.6, 0.5, 0.8),
586        draw_controller=btn,
587        texture=ba.gettexture('uiAtlas'),
588    )
589    item['price_widget'] = ba.textwidget(
590        parent=parent_widget,
591        text='',
592        position=(
593            b_pos[0] + b_width * 0.5 + b_offs_x,
594            b_pos[1] + b_height * price_v,
595        ),
596        transition_delay=delay,
597        scale=b_width * (1.0 / 300.0) * base_text_scale,
598        maxwidth=b_width * 0.9,
599        size=(0, 0),
600        h_align='center',
601        v_align='center',
602        draw_controller=btn,
603        color=(0.2, 1, 0.2, 1.0),
604    )
605    item['price_widget_left'] = ba.textwidget(
606        parent=parent_widget,
607        text='',
608        position=(
609            b_pos[0] + b_width * 0.33 + b_offs_x,
610            b_pos[1] + b_height * price_v,
611        ),
612        transition_delay=delay,
613        scale=b_width * (1.0 / 300.0) * base_text_scale,
614        maxwidth=b_width * 0.3,
615        size=(0, 0),
616        h_align='center',
617        v_align='center',
618        draw_controller=btn,
619        color=(0.2, 1, 0.2, 0.5),
620    )
621    item['price_widget_right'] = ba.textwidget(
622        parent=parent_widget,
623        text='',
624        position=(
625            b_pos[0] + b_width * 0.66 + b_offs_x,
626            b_pos[1] + b_height * price_v,
627        ),
628        transition_delay=delay,
629        scale=1.1 * b_width * (1.0 / 300.0) * base_text_scale,
630        maxwidth=b_width * 0.3,
631        size=(0, 0),
632        h_align='center',
633        v_align='center',
634        draw_controller=btn,
635        color=(0.2, 1, 0.2, 1.0),
636    )
637    item['price_slash_widget'] = ba.imagewidget(
638        parent=parent_widget,
639        position=(
640            b_pos[0] + b_width * 0.33 + b_offs_x - 36,
641            b_pos[1] + b_height * price_v - 35,
642        ),
643        transition_delay=delay,
644        texture=ba.gettexture('slash'),
645        opacity=0.0,
646        size=(70, 70),
647        draw_controller=btn,
648        color=(1, 0, 0),
649    )
650    badge_rad = 44
651    badge_center = (
652        b_pos[0] + b_width * 0.1 + b_offs_x,
653        b_pos[1] + b_height * 0.87,
654    )
655    item['sale_bg_widget'] = ba.imagewidget(
656        parent=parent_widget,
657        position=(badge_center[0] - badge_rad, badge_center[1] - badge_rad),
658        opacity=0.0,
659        transition_delay=delay,
660        texture=ba.gettexture('circleZigZag'),
661        draw_controller=btn,
662        size=(badge_rad * 2, badge_rad * 2),
663        color=(0.5, 0, 1),
664    )
665    item['sale_title_widget'] = ba.textwidget(
666        parent=parent_widget,
667        position=(badge_center[0], badge_center[1] + 12),
668        transition_delay=delay,
669        scale=1.0,
670        maxwidth=badge_rad * 1.6,
671        size=(0, 0),
672        h_align='center',
673        v_align='center',
674        draw_controller=btn,
675        shadow=0.0,
676        flatness=1.0,
677        color=(0, 1, 0),
678    )
679    item['sale_time_widget'] = ba.textwidget(
680        parent=parent_widget,
681        position=(badge_center[0], badge_center[1] - 12),
682        transition_delay=delay,
683        scale=0.7,
684        maxwidth=badge_rad * 1.6,
685        size=(0, 0),
686        h_align='center',
687        v_align='center',
688        draw_controller=btn,
689        shadow=0.0,
690        flatness=1.0,
691        color=(0.0, 1, 0.0, 1),
692    )