# Released under the MIT License. See LICENSE for details.
#
"""Provides a window to display game credits."""
from __future__ import annotations
import os
import logging
from typing import TYPE_CHECKING
import bauiv1 as bui
if TYPE_CHECKING:
from typing import Sequence
[docs]
class CreditsListWindow(bui.Window):
"""Window for displaying game credits."""
def __init__(self, origin_widget: bui.Widget | None = None):
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements
import json
bui.set_analytics_screen('Credits Window')
# if they provided an origin-widget, scale up from that
scale_origin: tuple[float, float] | None
if origin_widget is not None:
self._transition_out = 'out_scale'
scale_origin = origin_widget.get_screen_space_center()
transition = 'in_scale'
else:
self._transition_out = 'out_right'
scale_origin = None
transition = 'in_right'
assert bui.app.classic is not None
uiscale = bui.app.ui_v1.uiscale
width = 870 if uiscale is bui.UIScale.SMALL else 670
x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
height = 398 if uiscale is bui.UIScale.SMALL else 500
self._r = 'creditsWindow'
super().__init__(
root_widget=bui.containerwidget(
size=(width, height),
transition=transition,
toolbar_visibility='menu_minimal',
scale_origin_stack_offset=scale_origin,
scale=(
2.0
if uiscale is bui.UIScale.SMALL
else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0
),
stack_offset=(
(0, -8) if uiscale is bui.UIScale.SMALL else (0, 0)
),
)
)
if bui.app.ui_v1.use_toolbars and uiscale is bui.UIScale.SMALL:
bui.containerwidget(
edit=self._root_widget, on_cancel_call=self._back
)
else:
btn = bui.buttonwidget(
parent=self._root_widget,
position=(
40 + x_inset,
height - (68 if uiscale is bui.UIScale.SMALL else 62),
),
size=(140, 60),
scale=0.8,
label=bui.Lstr(resource='backText'),
button_type='back',
on_activate_call=self._back,
autoselect=True,
)
bui.containerwidget(edit=self._root_widget, cancel_button=btn)
bui.buttonwidget(
edit=btn,
button_type='backSmall',
position=(
40 + x_inset,
height - (68 if uiscale is bui.UIScale.SMALL else 62) + 5,
),
size=(60, 48),
label=bui.charstr(bui.SpecialChar.BACK),
)
bui.textwidget(
parent=self._root_widget,
position=(0, height - (59 if uiscale is bui.UIScale.SMALL else 54)),
size=(width, 30),
text=bui.Lstr(
resource=self._r + '.titleText',
subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
),
h_align='center',
color=bui.app.ui_v1.title_color,
maxwidth=330,
v_align='center',
)
scroll = bui.scrollwidget(
parent=self._root_widget,
position=(40 + x_inset, 35),
size=(width - (80 + 2 * x_inset), height - 100),
capture_arrows=True,
)
if bui.app.ui_v1.use_toolbars:
bui.widget(
edit=scroll,
right_widget=bui.get_special_widget('party_button'),
)
if uiscale is bui.UIScale.SMALL:
bui.widget(
edit=scroll,
left_widget=bui.get_special_widget('back_button'),
)
def _format_names(names2: Sequence[str], inset: float) -> str:
sval = ''
# measure a series since there's overlaps and stuff..
space_width = (
bui.get_string_width(' ' * 10, suppress_warning=True) / 10.0
)
spacing = 330.0
col1 = inset
col2 = col1 + spacing
col3 = col2 + spacing
line_width = 0.0
nline = ''
for name in names2:
# move to the next column (or row) and print
if line_width > col3:
sval += nline + '\n'
nline = ''
line_width = 0
if line_width > col2:
target = col3
elif line_width > col1:
target = col2
else:
target = col1
spacingstr = ' ' * int((target - line_width) / space_width)
nline += spacingstr
nline += name
line_width = bui.get_string_width(nline, suppress_warning=True)
if nline != '':
sval += nline + '\n'
return sval
sound_and_music = bui.Lstr(
resource=self._r + '.songCreditText'
).evaluate()
sound_and_music = sound_and_music.replace(
'${TITLE}', "'William Tell (Trumpet Entry)'"
)
sound_and_music = sound_and_music.replace(
'${PERFORMER}', 'The Apollo Symphony Orchestra'
)
sound_and_music = sound_and_music.replace(
'${PERFORMER}', 'The Apollo Symphony Orchestra'
)
sound_and_music = sound_and_music.replace(
'${COMPOSER}', 'Gioacchino Rossini'
)
sound_and_music = sound_and_music.replace('${ARRANGER}', 'Chris Worth')
sound_and_music = sound_and_music.replace('${PUBLISHER}', 'BMI')
sound_and_music = sound_and_music.replace(
'${SOURCE}', 'www.AudioSparx.com'
)
spc = ' '
sound_and_music = spc + sound_and_music.replace('\n', '\n' + spc)
names = [
'HubOfTheUniverseProd',
'Jovica',
'LG',
'Leady',
'Percy Duke',
'PhreaKsAccount',
'Pogotron',
'Rock Savage',
'anamorphosis',
'benboncan',
'cdrk',
'chipfork',
'guitarguy1985',
'jascha',
'joedeshon',
'loofa',
'm_O_m',
'mich3d',
'sandyrb',
'shakaharu',
'sirplus',
'stickman',
'thanvannispen',
'virotic',
'zimbot',
]
names.sort(key=lambda x: x.lower())
freesound_names = _format_names(names, 90)
try:
with open(
os.path.join(
bui.app.env.data_directory,
'ba_data',
'data',
'langdata.json',
),
encoding='utf-8',
) as infile:
translation_contributors = json.loads(infile.read())[
'translation_contributors'
]
except Exception:
logging.exception('Error reading translation contributors.')
translation_contributors = []
translation_names = _format_names(translation_contributors, 60)
# Need to bake this out and chop it up since we're passing our
# 65535 vertex limit for meshes..
# We can remove that limit once we drop support for GL ES2.. :-/
# (or add mesh splitting under the hood)
credits_text = (
' '
+ bui.Lstr(resource=self._r + '.codingGraphicsAudioText')
.evaluate()
.replace('${NAME}', 'Eric Froemling')
+ '\n'
'\n'
' '
+ bui.Lstr(resource=self._r + '.additionalAudioArtIdeasText')
.evaluate()
.replace('${NAME}', 'Raphael Suter')
+ '\n'
'\n'
' '
+ bui.Lstr(resource=self._r + '.soundAndMusicText').evaluate()
+ '\n'
'\n' + sound_and_music + '\n'
'\n'
' '
+ bui.Lstr(resource=self._r + '.publicDomainMusicViaText')
.evaluate()
.replace('${NAME}', 'Musopen.com')
+ '\n'
' '
+ bui.Lstr(resource=self._r + '.thanksEspeciallyToText')
.evaluate()
.replace('${NAME}', 'the US Army, Navy, and Marine Bands')
+ '\n'
'\n'
' '
+ bui.Lstr(resource=self._r + '.additionalMusicFromText')
.evaluate()
.replace('${NAME}', 'The YouTube Audio Library')
+ '\n'
'\n'
' '
+ bui.Lstr(resource=self._r + '.soundsText')
.evaluate()
.replace('${SOURCE}', 'Freesound.org')
+ '\n'
'\n' + freesound_names + '\n'
'\n'
' '
+ bui.Lstr(
resource=self._r + '.languageTranslationsText'
).evaluate()
+ '\n'
'\n'
+ '\n'.join(translation_names.splitlines()[:146])
+ '\n'.join(translation_names.splitlines()[146:])
+ '\n'
'\n'
' Shout Out to Awesome Mods / Modders / Contributors:\n\n'
' BombDash ModPack\n'
' TheMikirog & SoK - BombSquad Joyride Modpack\n'
' Mrmaxmeier - BombSquad-Community-Mod-Manager\n'
' Ritiek Malhotra \n'
' Dliwk\n'
' vishal332008\n'
' itsre3\n'
' Drooopyyy\n'
'\n'
' Holiday theme vector art designed by Freepik\n'
'\n'
' '
+ bui.Lstr(resource=self._r + '.specialThanksText').evaluate()
+ '\n'
'\n'
' Todd, Laura, and Robert Froemling\n'
' '
+ bui.Lstr(resource=self._r + '.allMyFamilyText')
.evaluate()
.replace('\n', '\n ')
+ '\n'
' '
+ bui.Lstr(
resource=self._r + '.whoeverInventedCoffeeText'
).evaluate()
+ '\n'
'\n'
' ' + bui.Lstr(resource=self._r + '.legalText').evaluate() + '\n'
'\n'
' '
+ bui.Lstr(resource=self._r + '.softwareBasedOnText')
.evaluate()
.replace('${NAME}', 'the Khronos Group')
+ '\n'
'\n'
' '
' www.ballistica.net\n'
)
txt = credits_text
lines = txt.splitlines()
line_height = 20
scale = 0.55
self._sub_width = width - 80
self._sub_height = line_height * len(lines) + 40
container = self._subcontainer = bui.containerwidget(
parent=scroll,
size=(self._sub_width, self._sub_height),
background=False,
claims_left_right=False,
claims_tab=False,
)
voffs = 0
for line in lines:
bui.textwidget(
parent=container,
padding=4,
color=(0.7, 0.9, 0.7, 1.0),
scale=scale,
flatness=1.0,
size=(0, 0),
position=(0, self._sub_height - 20 + voffs),
h_align='left',
v_align='top',
text=bui.Lstr(value=line),
)
voffs -= line_height
def _back(self) -> None:
from bauiv1lib.mainmenu import MainMenuWindow
# no-op if our underlying widget is dead or on its way out.
if not self._root_widget or self._root_widget.transitioning_out:
return
bui.containerwidget(
edit=self._root_widget, transition=self._transition_out
)
assert bui.app.classic is not None
bui.app.ui_v1.set_main_menu_window(
MainMenuWindow(transition='in_left').get_root_widget(),
from_window=self._root_widget,
)