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

Window for displaying game credits.

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