bauiv1lib.creditslist

Provides a window to display game credits.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides a window to display game credits."""
  4
  5from __future__ import annotations
  6
  7import os
  8import logging
  9from typing import TYPE_CHECKING
 10
 11import bauiv1 as bui
 12
 13if TYPE_CHECKING:
 14    from typing import Sequence
 15
 16
 17class CreditsListWindow(bui.Window):
 18    """Window for displaying game credits."""
 19
 20    def __init__(self, origin_widget: bui.Widget | None = None):
 21        # pylint: disable=too-many-locals
 22        # pylint: disable=too-many-statements
 23        import json
 24
 25        bui.set_analytics_screen('Credits Window')
 26
 27        # if they provided an origin-widget, scale up from that
 28        scale_origin: tuple[float, float] | None
 29        if origin_widget is not None:
 30            self._transition_out = 'out_scale'
 31            scale_origin = origin_widget.get_screen_space_center()
 32            transition = 'in_scale'
 33        else:
 34            self._transition_out = 'out_right'
 35            scale_origin = None
 36            transition = 'in_right'
 37
 38        assert bui.app.classic is not None
 39        uiscale = bui.app.ui_v1.uiscale
 40        width = 870 if uiscale is bui.UIScale.SMALL else 670
 41        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 42        height = 398 if uiscale is bui.UIScale.SMALL else 500
 43
 44        self._r = 'creditsWindow'
 45        super().__init__(
 46            root_widget=bui.containerwidget(
 47                size=(width, height),
 48                transition=transition,
 49                toolbar_visibility='menu_minimal',
 50                scale_origin_stack_offset=scale_origin,
 51                scale=(
 52                    2.0
 53                    if uiscale is bui.UIScale.SMALL
 54                    else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0
 55                ),
 56                stack_offset=(
 57                    (0, -8) if uiscale is bui.UIScale.SMALL else (0, 0)
 58                ),
 59            )
 60        )
 61
 62        if bui.app.ui_v1.use_toolbars and uiscale is bui.UIScale.SMALL:
 63            bui.containerwidget(
 64                edit=self._root_widget, on_cancel_call=self._back
 65            )
 66        else:
 67            btn = bui.buttonwidget(
 68                parent=self._root_widget,
 69                position=(
 70                    40 + x_inset,
 71                    height - (68 if uiscale is bui.UIScale.SMALL else 62),
 72                ),
 73                size=(140, 60),
 74                scale=0.8,
 75                label=bui.Lstr(resource='backText'),
 76                button_type='back',
 77                on_activate_call=self._back,
 78                autoselect=True,
 79            )
 80            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 81
 82            bui.buttonwidget(
 83                edit=btn,
 84                button_type='backSmall',
 85                position=(
 86                    40 + x_inset,
 87                    height - (68 if uiscale is bui.UIScale.SMALL else 62) + 5,
 88                ),
 89                size=(60, 48),
 90                label=bui.charstr(bui.SpecialChar.BACK),
 91            )
 92
 93        bui.textwidget(
 94            parent=self._root_widget,
 95            position=(0, height - (59 if uiscale is bui.UIScale.SMALL else 54)),
 96            size=(width, 30),
 97            text=bui.Lstr(
 98                resource=self._r + '.titleText',
 99                subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
100            ),
101            h_align='center',
102            color=bui.app.ui_v1.title_color,
103            maxwidth=330,
104            v_align='center',
105        )
106
107        scroll = bui.scrollwidget(
108            parent=self._root_widget,
109            position=(40 + x_inset, 35),
110            size=(width - (80 + 2 * x_inset), height - 100),
111            capture_arrows=True,
112        )
113
114        if bui.app.ui_v1.use_toolbars:
115            bui.widget(
116                edit=scroll,
117                right_widget=bui.get_special_widget('party_button'),
118            )
119            if uiscale is bui.UIScale.SMALL:
120                bui.widget(
121                    edit=scroll,
122                    left_widget=bui.get_special_widget('back_button'),
123                )
124
125        def _format_names(names2: Sequence[str], inset: float) -> str:
126            sval = ''
127            # measure a series since there's overlaps and stuff..
128            space_width = (
129                bui.get_string_width(' ' * 10, suppress_warning=True) / 10.0
130            )
131            spacing = 330.0
132            col1 = inset
133            col2 = col1 + spacing
134            col3 = col2 + spacing
135            line_width = 0.0
136            nline = ''
137            for name in names2:
138                # move to the next column (or row) and print
139                if line_width > col3:
140                    sval += nline + '\n'
141                    nline = ''
142                    line_width = 0
143
144                if line_width > col2:
145                    target = col3
146                elif line_width > col1:
147                    target = col2
148                else:
149                    target = col1
150                spacingstr = ' ' * int((target - line_width) / space_width)
151                nline += spacingstr
152                nline += name
153                line_width = bui.get_string_width(nline, suppress_warning=True)
154            if nline != '':
155                sval += nline + '\n'
156            return sval
157
158        sound_and_music = bui.Lstr(
159            resource=self._r + '.songCreditText'
160        ).evaluate()
161        sound_and_music = sound_and_music.replace(
162            '${TITLE}', "'William Tell (Trumpet Entry)'"
163        )
164        sound_and_music = sound_and_music.replace(
165            '${PERFORMER}', 'The Apollo Symphony Orchestra'
166        )
167        sound_and_music = sound_and_music.replace(
168            '${PERFORMER}', 'The Apollo Symphony Orchestra'
169        )
170        sound_and_music = sound_and_music.replace(
171            '${COMPOSER}', 'Gioacchino Rossini'
172        )
173        sound_and_music = sound_and_music.replace('${ARRANGER}', 'Chris Worth')
174        sound_and_music = sound_and_music.replace('${PUBLISHER}', 'BMI')
175        sound_and_music = sound_and_music.replace(
176            '${SOURCE}', 'www.AudioSparx.com'
177        )
178        spc = '     '
179        sound_and_music = spc + sound_and_music.replace('\n', '\n' + spc)
180        names = [
181            'HubOfTheUniverseProd',
182            'Jovica',
183            'LG',
184            'Leady',
185            'Percy Duke',
186            'PhreaKsAccount',
187            'Pogotron',
188            'Rock Savage',
189            'anamorphosis',
190            'benboncan',
191            'cdrk',
192            'chipfork',
193            'guitarguy1985',
194            'jascha',
195            'joedeshon',
196            'loofa',
197            'm_O_m',
198            'mich3d',
199            'sandyrb',
200            'shakaharu',
201            'sirplus',
202            'stickman',
203            'thanvannispen',
204            'virotic',
205            'zimbot',
206        ]
207        names.sort(key=lambda x: x.lower())
208        freesound_names = _format_names(names, 90)
209
210        try:
211            with open(
212                os.path.join(
213                    bui.app.env.data_directory,
214                    'ba_data',
215                    'data',
216                    'langdata.json',
217                ),
218                encoding='utf-8',
219            ) as infile:
220                translation_contributors = json.loads(infile.read())[
221                    'translation_contributors'
222                ]
223        except Exception:
224            logging.exception('Error reading translation contributors.')
225            translation_contributors = []
226
227        translation_names = _format_names(translation_contributors, 60)
228
229        # Need to bake this out and chop it up since we're passing our
230        # 65535 vertex limit for meshes..
231        # We can remove that limit once we drop support for GL ES2.. :-/
232        # (or add mesh splitting under the hood)
233        credits_text = (
234            '  '
235            + bui.Lstr(resource=self._r + '.codingGraphicsAudioText')
236            .evaluate()
237            .replace('${NAME}', 'Eric Froemling')
238            + '\n'
239            '\n'
240            '  '
241            + bui.Lstr(resource=self._r + '.additionalAudioArtIdeasText')
242            .evaluate()
243            .replace('${NAME}', 'Raphael Suter')
244            + '\n'
245            '\n'
246            '  '
247            + bui.Lstr(resource=self._r + '.soundAndMusicText').evaluate()
248            + '\n'
249            '\n' + sound_and_music + '\n'
250            '\n'
251            '     '
252            + bui.Lstr(resource=self._r + '.publicDomainMusicViaText')
253            .evaluate()
254            .replace('${NAME}', 'Musopen.com')
255            + '\n'
256            '        '
257            + bui.Lstr(resource=self._r + '.thanksEspeciallyToText')
258            .evaluate()
259            .replace('${NAME}', 'the US Army, Navy, and Marine Bands')
260            + '\n'
261            '\n'
262            '     '
263            + bui.Lstr(resource=self._r + '.additionalMusicFromText')
264            .evaluate()
265            .replace('${NAME}', 'The YouTube Audio Library')
266            + '\n'
267            '\n'
268            '     '
269            + bui.Lstr(resource=self._r + '.soundsText')
270            .evaluate()
271            .replace('${SOURCE}', 'Freesound.org')
272            + '\n'
273            '\n' + freesound_names + '\n'
274            '\n'
275            '  '
276            + bui.Lstr(
277                resource=self._r + '.languageTranslationsText'
278            ).evaluate()
279            + '\n'
280            '\n'
281            + '\n'.join(translation_names.splitlines()[:146])
282            + '\n'.join(translation_names.splitlines()[146:])
283            + '\n'
284            '\n'
285            '  Shout Out to Awesome Mods / Modders / Contributors:\n\n'
286            '     BombDash ModPack\n'
287            '     TheMikirog & SoK - BombSquad Joyride Modpack\n'
288            '     Mrmaxmeier - BombSquad-Community-Mod-Manager\n'
289            '     Ritiek Malhotra \n'
290            '     Dliwk\n'
291            '     vishal332008\n'
292            '     itsre3\n'
293            '     Drooopyyy\n'
294            '\n'
295            '  Holiday theme vector art designed by Freepik\n'
296            '\n'
297            '  '
298            + bui.Lstr(resource=self._r + '.specialThanksText').evaluate()
299            + '\n'
300            '\n'
301            '     Todd, Laura, and Robert Froemling\n'
302            '     '
303            + bui.Lstr(resource=self._r + '.allMyFamilyText')
304            .evaluate()
305            .replace('\n', '\n     ')
306            + '\n'
307            '     '
308            + bui.Lstr(
309                resource=self._r + '.whoeverInventedCoffeeText'
310            ).evaluate()
311            + '\n'
312            '\n'
313            '  ' + bui.Lstr(resource=self._r + '.legalText').evaluate() + '\n'
314            '\n'
315            '     '
316            + bui.Lstr(resource=self._r + '.softwareBasedOnText')
317            .evaluate()
318            .replace('${NAME}', 'the Khronos Group')
319            + '\n'
320            '\n'
321            '                                       '
322            '                      www.ballistica.net\n'
323        )
324
325        txt = credits_text
326        lines = txt.splitlines()
327        line_height = 20
328
329        scale = 0.55
330        self._sub_width = width - 80
331        self._sub_height = line_height * len(lines) + 40
332
333        container = self._subcontainer = bui.containerwidget(
334            parent=scroll,
335            size=(self._sub_width, self._sub_height),
336            background=False,
337            claims_left_right=False,
338            claims_tab=False,
339        )
340
341        voffs = 0
342        for line in lines:
343            bui.textwidget(
344                parent=container,
345                padding=4,
346                color=(0.7, 0.9, 0.7, 1.0),
347                scale=scale,
348                flatness=1.0,
349                size=(0, 0),
350                position=(0, self._sub_height - 20 + voffs),
351                h_align='left',
352                v_align='top',
353                text=bui.Lstr(value=line),
354            )
355            voffs -= line_height
356
357    def _back(self) -> None:
358        from bauiv1lib.mainmenu import MainMenuWindow
359
360        # no-op if our underlying widget is dead or on its way out.
361        if not self._root_widget or self._root_widget.transitioning_out:
362            return
363
364        bui.containerwidget(
365            edit=self._root_widget, transition=self._transition_out
366        )
367        assert bui.app.classic is not None
368        bui.app.ui_v1.set_main_menu_window(
369            MainMenuWindow(transition='in_left').get_root_widget(),
370            from_window=self._root_widget,
371        )
class CreditsListWindow(bauiv1._uitypes.Window):
 18class CreditsListWindow(bui.Window):
 19    """Window for displaying game credits."""
 20
 21    def __init__(self, origin_widget: bui.Widget | None = None):
 22        # pylint: disable=too-many-locals
 23        # pylint: disable=too-many-statements
 24        import json
 25
 26        bui.set_analytics_screen('Credits Window')
 27
 28        # if they provided an origin-widget, scale up from that
 29        scale_origin: tuple[float, float] | None
 30        if origin_widget is not None:
 31            self._transition_out = 'out_scale'
 32            scale_origin = origin_widget.get_screen_space_center()
 33            transition = 'in_scale'
 34        else:
 35            self._transition_out = 'out_right'
 36            scale_origin = None
 37            transition = 'in_right'
 38
 39        assert bui.app.classic is not None
 40        uiscale = bui.app.ui_v1.uiscale
 41        width = 870 if uiscale is bui.UIScale.SMALL else 670
 42        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 43        height = 398 if uiscale is bui.UIScale.SMALL else 500
 44
 45        self._r = 'creditsWindow'
 46        super().__init__(
 47            root_widget=bui.containerwidget(
 48                size=(width, height),
 49                transition=transition,
 50                toolbar_visibility='menu_minimal',
 51                scale_origin_stack_offset=scale_origin,
 52                scale=(
 53                    2.0
 54                    if uiscale is bui.UIScale.SMALL
 55                    else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0
 56                ),
 57                stack_offset=(
 58                    (0, -8) if uiscale is bui.UIScale.SMALL else (0, 0)
 59                ),
 60            )
 61        )
 62
 63        if bui.app.ui_v1.use_toolbars and uiscale is bui.UIScale.SMALL:
 64            bui.containerwidget(
 65                edit=self._root_widget, on_cancel_call=self._back
 66            )
 67        else:
 68            btn = bui.buttonwidget(
 69                parent=self._root_widget,
 70                position=(
 71                    40 + x_inset,
 72                    height - (68 if uiscale is bui.UIScale.SMALL else 62),
 73                ),
 74                size=(140, 60),
 75                scale=0.8,
 76                label=bui.Lstr(resource='backText'),
 77                button_type='back',
 78                on_activate_call=self._back,
 79                autoselect=True,
 80            )
 81            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 82
 83            bui.buttonwidget(
 84                edit=btn,
 85                button_type='backSmall',
 86                position=(
 87                    40 + x_inset,
 88                    height - (68 if uiscale is bui.UIScale.SMALL else 62) + 5,
 89                ),
 90                size=(60, 48),
 91                label=bui.charstr(bui.SpecialChar.BACK),
 92            )
 93
 94        bui.textwidget(
 95            parent=self._root_widget,
 96            position=(0, height - (59 if uiscale is bui.UIScale.SMALL else 54)),
 97            size=(width, 30),
 98            text=bui.Lstr(
 99                resource=self._r + '.titleText',
100                subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
101            ),
102            h_align='center',
103            color=bui.app.ui_v1.title_color,
104            maxwidth=330,
105            v_align='center',
106        )
107
108        scroll = bui.scrollwidget(
109            parent=self._root_widget,
110            position=(40 + x_inset, 35),
111            size=(width - (80 + 2 * x_inset), height - 100),
112            capture_arrows=True,
113        )
114
115        if bui.app.ui_v1.use_toolbars:
116            bui.widget(
117                edit=scroll,
118                right_widget=bui.get_special_widget('party_button'),
119            )
120            if uiscale is bui.UIScale.SMALL:
121                bui.widget(
122                    edit=scroll,
123                    left_widget=bui.get_special_widget('back_button'),
124                )
125
126        def _format_names(names2: Sequence[str], inset: float) -> str:
127            sval = ''
128            # measure a series since there's overlaps and stuff..
129            space_width = (
130                bui.get_string_width(' ' * 10, suppress_warning=True) / 10.0
131            )
132            spacing = 330.0
133            col1 = inset
134            col2 = col1 + spacing
135            col3 = col2 + spacing
136            line_width = 0.0
137            nline = ''
138            for name in names2:
139                # move to the next column (or row) and print
140                if line_width > col3:
141                    sval += nline + '\n'
142                    nline = ''
143                    line_width = 0
144
145                if line_width > col2:
146                    target = col3
147                elif line_width > col1:
148                    target = col2
149                else:
150                    target = col1
151                spacingstr = ' ' * int((target - line_width) / space_width)
152                nline += spacingstr
153                nline += name
154                line_width = bui.get_string_width(nline, suppress_warning=True)
155            if nline != '':
156                sval += nline + '\n'
157            return sval
158
159        sound_and_music = bui.Lstr(
160            resource=self._r + '.songCreditText'
161        ).evaluate()
162        sound_and_music = sound_and_music.replace(
163            '${TITLE}', "'William Tell (Trumpet Entry)'"
164        )
165        sound_and_music = sound_and_music.replace(
166            '${PERFORMER}', 'The Apollo Symphony Orchestra'
167        )
168        sound_and_music = sound_and_music.replace(
169            '${PERFORMER}', 'The Apollo Symphony Orchestra'
170        )
171        sound_and_music = sound_and_music.replace(
172            '${COMPOSER}', 'Gioacchino Rossini'
173        )
174        sound_and_music = sound_and_music.replace('${ARRANGER}', 'Chris Worth')
175        sound_and_music = sound_and_music.replace('${PUBLISHER}', 'BMI')
176        sound_and_music = sound_and_music.replace(
177            '${SOURCE}', 'www.AudioSparx.com'
178        )
179        spc = '     '
180        sound_and_music = spc + sound_and_music.replace('\n', '\n' + spc)
181        names = [
182            'HubOfTheUniverseProd',
183            'Jovica',
184            'LG',
185            'Leady',
186            'Percy Duke',
187            'PhreaKsAccount',
188            'Pogotron',
189            'Rock Savage',
190            'anamorphosis',
191            'benboncan',
192            'cdrk',
193            'chipfork',
194            'guitarguy1985',
195            'jascha',
196            'joedeshon',
197            'loofa',
198            'm_O_m',
199            'mich3d',
200            'sandyrb',
201            'shakaharu',
202            'sirplus',
203            'stickman',
204            'thanvannispen',
205            'virotic',
206            'zimbot',
207        ]
208        names.sort(key=lambda x: x.lower())
209        freesound_names = _format_names(names, 90)
210
211        try:
212            with open(
213                os.path.join(
214                    bui.app.env.data_directory,
215                    'ba_data',
216                    'data',
217                    'langdata.json',
218                ),
219                encoding='utf-8',
220            ) as infile:
221                translation_contributors = json.loads(infile.read())[
222                    'translation_contributors'
223                ]
224        except Exception:
225            logging.exception('Error reading translation contributors.')
226            translation_contributors = []
227
228        translation_names = _format_names(translation_contributors, 60)
229
230        # Need to bake this out and chop it up since we're passing our
231        # 65535 vertex limit for meshes..
232        # We can remove that limit once we drop support for GL ES2.. :-/
233        # (or add mesh splitting under the hood)
234        credits_text = (
235            '  '
236            + bui.Lstr(resource=self._r + '.codingGraphicsAudioText')
237            .evaluate()
238            .replace('${NAME}', 'Eric Froemling')
239            + '\n'
240            '\n'
241            '  '
242            + bui.Lstr(resource=self._r + '.additionalAudioArtIdeasText')
243            .evaluate()
244            .replace('${NAME}', 'Raphael Suter')
245            + '\n'
246            '\n'
247            '  '
248            + bui.Lstr(resource=self._r + '.soundAndMusicText').evaluate()
249            + '\n'
250            '\n' + sound_and_music + '\n'
251            '\n'
252            '     '
253            + bui.Lstr(resource=self._r + '.publicDomainMusicViaText')
254            .evaluate()
255            .replace('${NAME}', 'Musopen.com')
256            + '\n'
257            '        '
258            + bui.Lstr(resource=self._r + '.thanksEspeciallyToText')
259            .evaluate()
260            .replace('${NAME}', 'the US Army, Navy, and Marine Bands')
261            + '\n'
262            '\n'
263            '     '
264            + bui.Lstr(resource=self._r + '.additionalMusicFromText')
265            .evaluate()
266            .replace('${NAME}', 'The YouTube Audio Library')
267            + '\n'
268            '\n'
269            '     '
270            + bui.Lstr(resource=self._r + '.soundsText')
271            .evaluate()
272            .replace('${SOURCE}', 'Freesound.org')
273            + '\n'
274            '\n' + freesound_names + '\n'
275            '\n'
276            '  '
277            + bui.Lstr(
278                resource=self._r + '.languageTranslationsText'
279            ).evaluate()
280            + '\n'
281            '\n'
282            + '\n'.join(translation_names.splitlines()[:146])
283            + '\n'.join(translation_names.splitlines()[146:])
284            + '\n'
285            '\n'
286            '  Shout Out to Awesome Mods / Modders / Contributors:\n\n'
287            '     BombDash ModPack\n'
288            '     TheMikirog & SoK - BombSquad Joyride Modpack\n'
289            '     Mrmaxmeier - BombSquad-Community-Mod-Manager\n'
290            '     Ritiek Malhotra \n'
291            '     Dliwk\n'
292            '     vishal332008\n'
293            '     itsre3\n'
294            '     Drooopyyy\n'
295            '\n'
296            '  Holiday theme vector art designed by Freepik\n'
297            '\n'
298            '  '
299            + bui.Lstr(resource=self._r + '.specialThanksText').evaluate()
300            + '\n'
301            '\n'
302            '     Todd, Laura, and Robert Froemling\n'
303            '     '
304            + bui.Lstr(resource=self._r + '.allMyFamilyText')
305            .evaluate()
306            .replace('\n', '\n     ')
307            + '\n'
308            '     '
309            + bui.Lstr(
310                resource=self._r + '.whoeverInventedCoffeeText'
311            ).evaluate()
312            + '\n'
313            '\n'
314            '  ' + bui.Lstr(resource=self._r + '.legalText').evaluate() + '\n'
315            '\n'
316            '     '
317            + bui.Lstr(resource=self._r + '.softwareBasedOnText')
318            .evaluate()
319            .replace('${NAME}', 'the Khronos Group')
320            + '\n'
321            '\n'
322            '                                       '
323            '                      www.ballistica.net\n'
324        )
325
326        txt = credits_text
327        lines = txt.splitlines()
328        line_height = 20
329
330        scale = 0.55
331        self._sub_width = width - 80
332        self._sub_height = line_height * len(lines) + 40
333
334        container = self._subcontainer = bui.containerwidget(
335            parent=scroll,
336            size=(self._sub_width, self._sub_height),
337            background=False,
338            claims_left_right=False,
339            claims_tab=False,
340        )
341
342        voffs = 0
343        for line in lines:
344            bui.textwidget(
345                parent=container,
346                padding=4,
347                color=(0.7, 0.9, 0.7, 1.0),
348                scale=scale,
349                flatness=1.0,
350                size=(0, 0),
351                position=(0, self._sub_height - 20 + voffs),
352                h_align='left',
353                v_align='top',
354                text=bui.Lstr(value=line),
355            )
356            voffs -= line_height
357
358    def _back(self) -> None:
359        from bauiv1lib.mainmenu import MainMenuWindow
360
361        # no-op if our underlying widget is dead or on its way out.
362        if not self._root_widget or self._root_widget.transitioning_out:
363            return
364
365        bui.containerwidget(
366            edit=self._root_widget, transition=self._transition_out
367        )
368        assert bui.app.classic is not None
369        bui.app.ui_v1.set_main_menu_window(
370            MainMenuWindow(transition='in_left').get_root_widget(),
371            from_window=self._root_widget,
372        )

Window for displaying game credits.

CreditsListWindow(origin_widget: _bauiv1.Widget | None = None)
 21    def __init__(self, origin_widget: bui.Widget | None = None):
 22        # pylint: disable=too-many-locals
 23        # pylint: disable=too-many-statements
 24        import json
 25
 26        bui.set_analytics_screen('Credits Window')
 27
 28        # if they provided an origin-widget, scale up from that
 29        scale_origin: tuple[float, float] | None
 30        if origin_widget is not None:
 31            self._transition_out = 'out_scale'
 32            scale_origin = origin_widget.get_screen_space_center()
 33            transition = 'in_scale'
 34        else:
 35            self._transition_out = 'out_right'
 36            scale_origin = None
 37            transition = 'in_right'
 38
 39        assert bui.app.classic is not None
 40        uiscale = bui.app.ui_v1.uiscale
 41        width = 870 if uiscale is bui.UIScale.SMALL else 670
 42        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 43        height = 398 if uiscale is bui.UIScale.SMALL else 500
 44
 45        self._r = 'creditsWindow'
 46        super().__init__(
 47            root_widget=bui.containerwidget(
 48                size=(width, height),
 49                transition=transition,
 50                toolbar_visibility='menu_minimal',
 51                scale_origin_stack_offset=scale_origin,
 52                scale=(
 53                    2.0
 54                    if uiscale is bui.UIScale.SMALL
 55                    else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0
 56                ),
 57                stack_offset=(
 58                    (0, -8) if uiscale is bui.UIScale.SMALL else (0, 0)
 59                ),
 60            )
 61        )
 62
 63        if bui.app.ui_v1.use_toolbars and uiscale is bui.UIScale.SMALL:
 64            bui.containerwidget(
 65                edit=self._root_widget, on_cancel_call=self._back
 66            )
 67        else:
 68            btn = bui.buttonwidget(
 69                parent=self._root_widget,
 70                position=(
 71                    40 + x_inset,
 72                    height - (68 if uiscale is bui.UIScale.SMALL else 62),
 73                ),
 74                size=(140, 60),
 75                scale=0.8,
 76                label=bui.Lstr(resource='backText'),
 77                button_type='back',
 78                on_activate_call=self._back,
 79                autoselect=True,
 80            )
 81            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 82
 83            bui.buttonwidget(
 84                edit=btn,
 85                button_type='backSmall',
 86                position=(
 87                    40 + x_inset,
 88                    height - (68 if uiscale is bui.UIScale.SMALL else 62) + 5,
 89                ),
 90                size=(60, 48),
 91                label=bui.charstr(bui.SpecialChar.BACK),
 92            )
 93
 94        bui.textwidget(
 95            parent=self._root_widget,
 96            position=(0, height - (59 if uiscale is bui.UIScale.SMALL else 54)),
 97            size=(width, 30),
 98            text=bui.Lstr(
 99                resource=self._r + '.titleText',
100                subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
101            ),
102            h_align='center',
103            color=bui.app.ui_v1.title_color,
104            maxwidth=330,
105            v_align='center',
106        )
107
108        scroll = bui.scrollwidget(
109            parent=self._root_widget,
110            position=(40 + x_inset, 35),
111            size=(width - (80 + 2 * x_inset), height - 100),
112            capture_arrows=True,
113        )
114
115        if bui.app.ui_v1.use_toolbars:
116            bui.widget(
117                edit=scroll,
118                right_widget=bui.get_special_widget('party_button'),
119            )
120            if uiscale is bui.UIScale.SMALL:
121                bui.widget(
122                    edit=scroll,
123                    left_widget=bui.get_special_widget('back_button'),
124                )
125
126        def _format_names(names2: Sequence[str], inset: float) -> str:
127            sval = ''
128            # measure a series since there's overlaps and stuff..
129            space_width = (
130                bui.get_string_width(' ' * 10, suppress_warning=True) / 10.0
131            )
132            spacing = 330.0
133            col1 = inset
134            col2 = col1 + spacing
135            col3 = col2 + spacing
136            line_width = 0.0
137            nline = ''
138            for name in names2:
139                # move to the next column (or row) and print
140                if line_width > col3:
141                    sval += nline + '\n'
142                    nline = ''
143                    line_width = 0
144
145                if line_width > col2:
146                    target = col3
147                elif line_width > col1:
148                    target = col2
149                else:
150                    target = col1
151                spacingstr = ' ' * int((target - line_width) / space_width)
152                nline += spacingstr
153                nline += name
154                line_width = bui.get_string_width(nline, suppress_warning=True)
155            if nline != '':
156                sval += nline + '\n'
157            return sval
158
159        sound_and_music = bui.Lstr(
160            resource=self._r + '.songCreditText'
161        ).evaluate()
162        sound_and_music = sound_and_music.replace(
163            '${TITLE}', "'William Tell (Trumpet Entry)'"
164        )
165        sound_and_music = sound_and_music.replace(
166            '${PERFORMER}', 'The Apollo Symphony Orchestra'
167        )
168        sound_and_music = sound_and_music.replace(
169            '${PERFORMER}', 'The Apollo Symphony Orchestra'
170        )
171        sound_and_music = sound_and_music.replace(
172            '${COMPOSER}', 'Gioacchino Rossini'
173        )
174        sound_and_music = sound_and_music.replace('${ARRANGER}', 'Chris Worth')
175        sound_and_music = sound_and_music.replace('${PUBLISHER}', 'BMI')
176        sound_and_music = sound_and_music.replace(
177            '${SOURCE}', 'www.AudioSparx.com'
178        )
179        spc = '     '
180        sound_and_music = spc + sound_and_music.replace('\n', '\n' + spc)
181        names = [
182            'HubOfTheUniverseProd',
183            'Jovica',
184            'LG',
185            'Leady',
186            'Percy Duke',
187            'PhreaKsAccount',
188            'Pogotron',
189            'Rock Savage',
190            'anamorphosis',
191            'benboncan',
192            'cdrk',
193            'chipfork',
194            'guitarguy1985',
195            'jascha',
196            'joedeshon',
197            'loofa',
198            'm_O_m',
199            'mich3d',
200            'sandyrb',
201            'shakaharu',
202            'sirplus',
203            'stickman',
204            'thanvannispen',
205            'virotic',
206            'zimbot',
207        ]
208        names.sort(key=lambda x: x.lower())
209        freesound_names = _format_names(names, 90)
210
211        try:
212            with open(
213                os.path.join(
214                    bui.app.env.data_directory,
215                    'ba_data',
216                    'data',
217                    'langdata.json',
218                ),
219                encoding='utf-8',
220            ) as infile:
221                translation_contributors = json.loads(infile.read())[
222                    'translation_contributors'
223                ]
224        except Exception:
225            logging.exception('Error reading translation contributors.')
226            translation_contributors = []
227
228        translation_names = _format_names(translation_contributors, 60)
229
230        # Need to bake this out and chop it up since we're passing our
231        # 65535 vertex limit for meshes..
232        # We can remove that limit once we drop support for GL ES2.. :-/
233        # (or add mesh splitting under the hood)
234        credits_text = (
235            '  '
236            + bui.Lstr(resource=self._r + '.codingGraphicsAudioText')
237            .evaluate()
238            .replace('${NAME}', 'Eric Froemling')
239            + '\n'
240            '\n'
241            '  '
242            + bui.Lstr(resource=self._r + '.additionalAudioArtIdeasText')
243            .evaluate()
244            .replace('${NAME}', 'Raphael Suter')
245            + '\n'
246            '\n'
247            '  '
248            + bui.Lstr(resource=self._r + '.soundAndMusicText').evaluate()
249            + '\n'
250            '\n' + sound_and_music + '\n'
251            '\n'
252            '     '
253            + bui.Lstr(resource=self._r + '.publicDomainMusicViaText')
254            .evaluate()
255            .replace('${NAME}', 'Musopen.com')
256            + '\n'
257            '        '
258            + bui.Lstr(resource=self._r + '.thanksEspeciallyToText')
259            .evaluate()
260            .replace('${NAME}', 'the US Army, Navy, and Marine Bands')
261            + '\n'
262            '\n'
263            '     '
264            + bui.Lstr(resource=self._r + '.additionalMusicFromText')
265            .evaluate()
266            .replace('${NAME}', 'The YouTube Audio Library')
267            + '\n'
268            '\n'
269            '     '
270            + bui.Lstr(resource=self._r + '.soundsText')
271            .evaluate()
272            .replace('${SOURCE}', 'Freesound.org')
273            + '\n'
274            '\n' + freesound_names + '\n'
275            '\n'
276            '  '
277            + bui.Lstr(
278                resource=self._r + '.languageTranslationsText'
279            ).evaluate()
280            + '\n'
281            '\n'
282            + '\n'.join(translation_names.splitlines()[:146])
283            + '\n'.join(translation_names.splitlines()[146:])
284            + '\n'
285            '\n'
286            '  Shout Out to Awesome Mods / Modders / Contributors:\n\n'
287            '     BombDash ModPack\n'
288            '     TheMikirog & SoK - BombSquad Joyride Modpack\n'
289            '     Mrmaxmeier - BombSquad-Community-Mod-Manager\n'
290            '     Ritiek Malhotra \n'
291            '     Dliwk\n'
292            '     vishal332008\n'
293            '     itsre3\n'
294            '     Drooopyyy\n'
295            '\n'
296            '  Holiday theme vector art designed by Freepik\n'
297            '\n'
298            '  '
299            + bui.Lstr(resource=self._r + '.specialThanksText').evaluate()
300            + '\n'
301            '\n'
302            '     Todd, Laura, and Robert Froemling\n'
303            '     '
304            + bui.Lstr(resource=self._r + '.allMyFamilyText')
305            .evaluate()
306            .replace('\n', '\n     ')
307            + '\n'
308            '     '
309            + bui.Lstr(
310                resource=self._r + '.whoeverInventedCoffeeText'
311            ).evaluate()
312            + '\n'
313            '\n'
314            '  ' + bui.Lstr(resource=self._r + '.legalText').evaluate() + '\n'
315            '\n'
316            '     '
317            + bui.Lstr(resource=self._r + '.softwareBasedOnText')
318            .evaluate()
319            .replace('${NAME}', 'the Khronos Group')
320            + '\n'
321            '\n'
322            '                                       '
323            '                      www.ballistica.net\n'
324        )
325
326        txt = credits_text
327        lines = txt.splitlines()
328        line_height = 20
329
330        scale = 0.55
331        self._sub_width = width - 80
332        self._sub_height = line_height * len(lines) + 40
333
334        container = self._subcontainer = bui.containerwidget(
335            parent=scroll,
336            size=(self._sub_width, self._sub_height),
337            background=False,
338            claims_left_right=False,
339            claims_tab=False,
340        )
341
342        voffs = 0
343        for line in lines:
344            bui.textwidget(
345                parent=container,
346                padding=4,
347                color=(0.7, 0.9, 0.7, 1.0),
348                scale=scale,
349                flatness=1.0,
350                size=(0, 0),
351                position=(0, self._sub_height - 20 + voffs),
352                h_align='left',
353                v_align='top',
354                text=bui.Lstr(value=line),
355            )
356            voffs -= line_height
Inherited Members
bauiv1._uitypes.Window
get_root_widget