vh-bombsquad-modded-server-.../dist/ba_data/python/bastd/ui/account/viewer.py
2024-06-06 19:50:58 +05:30

591 lines
22 KiB
Python

# Released under the MIT License. See LICENSE for details.
#
"""Provides a popup for displaying info about any account."""
from __future__ import annotations
from typing import TYPE_CHECKING
import ba
import ba.internal
from bastd.ui import popup
if TYPE_CHECKING:
from typing import Any
class AccountViewerWindow(popup.PopupWindow):
"""Popup window that displays info for an account."""
def __init__(
self,
account_id: str,
profile_id: str | None = None,
position: tuple[float, float] = (0.0, 0.0),
scale: float | None = None,
offset: tuple[float, float] = (0.0, 0.0),
):
from ba.internal import is_browser_likely_available, master_server_get
self._account_id = account_id
self._profile_id = profile_id
uiscale = ba.app.ui.uiscale
if scale is None:
scale = (
2.6
if uiscale is ba.UIScale.SMALL
else 1.8
if uiscale is ba.UIScale.MEDIUM
else 1.4
)
self._transitioning_out = False
self._width = 400
self._height = (
300
if uiscale is ba.UIScale.SMALL
else 400
if uiscale is ba.UIScale.MEDIUM
else 450
)
self._subcontainer: ba.Widget | None = None
bg_color = (0.5, 0.4, 0.6)
# Creates our _root_widget.
popup.PopupWindow.__init__(
self,
position=position,
size=(self._width, self._height),
scale=scale,
bg_color=bg_color,
offset=offset,
)
self._cancel_button = ba.buttonwidget(
parent=self.root_widget,
position=(50, self._height - 30),
size=(50, 50),
scale=0.5,
label='',
color=bg_color,
on_activate_call=self._on_cancel_press,
autoselect=True,
icon=ba.gettexture('crossOut'),
iconscale=1.2,
)
self._title_text = ba.textwidget(
parent=self.root_widget,
position=(self._width * 0.5, self._height - 20),
size=(0, 0),
h_align='center',
v_align='center',
scale=0.6,
text=ba.Lstr(resource='playerInfoText'),
maxwidth=200,
color=(0.7, 0.7, 0.7, 0.7),
)
self._scrollwidget = ba.scrollwidget(
parent=self.root_widget,
size=(self._width - 60, self._height - 70),
position=(30, 30),
capture_arrows=True,
simple_culling_v=10,
)
ba.widget(edit=self._scrollwidget, autoselect=True)
self._loading_text = ba.textwidget(
parent=self._scrollwidget,
scale=0.5,
text=ba.Lstr(
value='${A}...',
subs=[('${A}', ba.Lstr(resource='loadingText'))],
),
size=(self._width - 60, 100),
h_align='center',
v_align='center',
)
# In cases where the user most likely has a browser/email, lets
# offer a 'report this user' button.
if (
is_browser_likely_available()
and ba.internal.get_v1_account_misc_read_val(
'showAccountExtrasMenu', False
)
):
self._extras_menu_button = ba.buttonwidget(
parent=self.root_widget,
size=(20, 20),
position=(self._width - 60, self._height - 30),
autoselect=True,
label='...',
button_type='square',
color=(0.64, 0.52, 0.69),
textcolor=(0.57, 0.47, 0.57),
on_activate_call=self._on_extras_menu_press,
)
ba.containerwidget(
edit=self.root_widget, cancel_button=self._cancel_button
)
master_server_get(
'bsAccountInfo',
{
'buildNumber': ba.app.build_number,
'accountID': self._account_id,
'profileID': self._profile_id,
},
callback=ba.WeakCall(self._on_query_response),
)
def popup_menu_selected_choice(
self, window: popup.PopupMenu, choice: str
) -> None:
"""Called when a menu entry is selected."""
del window # Unused arg.
if choice == 'more':
self._on_more_press()
elif choice == 'report':
self._on_report_press()
elif choice == 'ban':
self._on_ban_press()
else:
print('ERROR: unknown account info extras menu item:', choice)
def popup_menu_closing(self, window: popup.PopupMenu) -> None:
"""Called when the popup menu is closing."""
def _on_extras_menu_press(self) -> None:
choices = ['more', 'report']
choices_display = [
ba.Lstr(resource='coopSelectWindow.seeMoreText'),
ba.Lstr(resource='reportThisPlayerText'),
]
is_admin = False
if is_admin:
ba.screenmessage('TEMP FORCING ADMIN ON')
choices.append('ban')
choices_display.append(ba.Lstr(resource='banThisPlayerText'))
uiscale = ba.app.ui.uiscale
popup.PopupMenuWindow(
position=self._extras_menu_button.get_screen_space_center(),
scale=(
2.3
if uiscale is ba.UIScale.SMALL
else 1.65
if uiscale is ba.UIScale.MEDIUM
else 1.23
),
choices=choices,
choices_display=choices_display,
current_choice='more',
delegate=self,
)
def _on_ban_press(self) -> None:
ba.internal.add_transaction(
{'type': 'BAN_ACCOUNT', 'account': self._account_id}
)
ba.internal.run_transactions()
def _on_report_press(self) -> None:
from bastd.ui import report
report.ReportPlayerWindow(
self._account_id, origin_widget=self._extras_menu_button
)
def _on_more_press(self) -> None:
ba.open_url(
ba.internal.get_master_server_address()
+ '/highscores?profile='
+ self._account_id
)
def _on_query_response(self, data: dict[str, Any] | None) -> None:
# FIXME: Tidy this up.
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
# pylint: disable=too-many-nested-blocks
if data is None:
ba.textwidget(
edit=self._loading_text,
text=ba.Lstr(resource='internal.unavailableNoConnectionText'),
)
else:
try:
self._loading_text.delete()
trophystr = ''
try:
trophystr = data['trophies']
num = 10
chunks = [
trophystr[i : i + num]
for i in range(0, len(trophystr), num)
]
trophystr = '\n\n'.join(chunks)
if trophystr == '':
trophystr = '-'
except Exception:
ba.print_exception('Error displaying trophies.')
account_name_spacing = 15
tscale = 0.65
ts_height = ba.internal.get_string_height(
trophystr, suppress_warning=True
)
sub_width = self._width - 80
sub_height = (
200
+ ts_height * tscale
+ account_name_spacing * len(data['accountDisplayStrings'])
)
self._subcontainer = ba.containerwidget(
parent=self._scrollwidget,
size=(sub_width, sub_height),
background=False,
)
v = sub_height - 20
title_scale = 0.37
center = 0.3
maxwidth_scale = 0.45
showing_character = False
if data['profileDisplayString'] is not None:
tint_color = (1, 1, 1)
try:
if data['profile'] is not None:
profile = data['profile']
character = ba.app.spaz_appearances.get(
profile['character'], None
)
if character is not None:
tint_color = (
profile['color']
if 'color' in profile
else (1, 1, 1)
)
tint2_color = (
profile['highlight']
if 'highlight' in profile
else (1, 1, 1)
)
icon_tex = character.icon_texture
tint_tex = character.icon_mask_texture
mask_texture = ba.gettexture(
'characterIconMask'
)
ba.imagewidget(
parent=self._subcontainer,
position=(sub_width * center - 40, v - 80),
size=(80, 80),
color=(1, 1, 1),
mask_texture=mask_texture,
texture=ba.gettexture(icon_tex),
tint_texture=ba.gettexture(tint_tex),
tint_color=tint_color,
tint2_color=tint2_color,
)
v -= 95
except Exception:
ba.print_exception('Error displaying character.')
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.9,
color=ba.safecolor(tint_color, 0.7),
shadow=1.0,
text=ba.Lstr(value=data['profileDisplayString']),
maxwidth=sub_width * maxwidth_scale * 0.75,
)
showing_character = True
v -= 33
center = 0.75 if showing_character else 0.5
maxwidth_scale = 0.45 if showing_character else 0.9
v = sub_height - 20
if len(data['accountDisplayStrings']) <= 1:
account_title = ba.Lstr(
resource='settingsWindow.accountText'
)
else:
account_title = ba.Lstr(
resource='accountSettingsWindow.accountsText',
fallback_resource='settingsWindow.accountText',
)
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text=account_title,
maxwidth=sub_width * maxwidth_scale,
)
draw_small = (
showing_character or len(data['accountDisplayStrings']) > 1
)
v -= 14 if draw_small else 20
for account_string in data['accountDisplayStrings']:
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55 if draw_small else 0.8,
text=account_string,
maxwidth=sub_width * maxwidth_scale,
)
v -= account_name_spacing
v += account_name_spacing
v -= 25 if showing_character else 29
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text=ba.Lstr(resource='rankText'),
maxwidth=sub_width * maxwidth_scale,
)
v -= 14
if data['rank'] is None:
rank_str = '-'
suffix_offset = None
else:
str_raw = ba.Lstr(
resource='league.rankInLeagueText'
).evaluate()
# FIXME: Would be nice to not have to eval this.
rank_str = ba.Lstr(
resource='league.rankInLeagueText',
subs=[
('${RANK}', str(data['rank'][2])),
(
'${NAME}',
ba.Lstr(
translate=('leagueNames', data['rank'][0])
),
),
('${SUFFIX}', ''),
],
).evaluate()
rank_str_width = min(
sub_width * maxwidth_scale,
ba.internal.get_string_width(
rank_str, suppress_warning=True
)
* 0.55,
)
# Only tack our suffix on if its at the end and only for
# non-diamond leagues.
if (
str_raw.endswith('${SUFFIX}')
and data['rank'][0] != 'Diamond'
):
suffix_offset = rank_str_width * 0.5 + 2
else:
suffix_offset = None
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55,
text=rank_str,
maxwidth=sub_width * maxwidth_scale,
)
if suffix_offset is not None:
assert data['rank'] is not None
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center + suffix_offset, v + 3),
h_align='left',
v_align='center',
scale=0.29,
flatness=1.0,
text='[' + str(data['rank'][1]) + ']',
)
v -= 14
str_raw = ba.Lstr(resource='league.rankInLeagueText').evaluate()
old_offs = -50
prev_ranks_shown = 0
for prev_rank in data['prevRanks']:
rank_str = ba.Lstr(
value='${S}: ${I}',
subs=[
(
'${S}',
ba.Lstr(
resource='league.seasonText',
subs=[('${NUMBER}', str(prev_rank[0]))],
),
),
(
'${I}',
ba.Lstr(
resource='league.rankInLeagueText',
subs=[
('${RANK}', str(prev_rank[3])),
(
'${NAME}',
ba.Lstr(
translate=(
'leagueNames',
prev_rank[1],
)
),
),
('${SUFFIX}', ''),
],
),
),
],
).evaluate()
rank_str_width = min(
sub_width * maxwidth_scale,
ba.internal.get_string_width(
rank_str, suppress_warning=True
)
* 0.3,
)
# Only tack our suffix on if its at the end and only for
# non-diamond leagues.
if (
str_raw.endswith('${SUFFIX}')
and prev_rank[1] != 'Diamond'
):
suffix_offset = rank_str_width + 2
else:
suffix_offset = None
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center + old_offs, v),
h_align='left',
v_align='center',
scale=0.3,
text=rank_str,
flatness=1.0,
maxwidth=sub_width * maxwidth_scale,
)
if suffix_offset is not None:
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(
sub_width * center + old_offs + suffix_offset,
v + 1,
),
h_align='left',
v_align='center',
scale=0.20,
flatness=1.0,
text='[' + str(prev_rank[2]) + ']',
)
prev_ranks_shown += 1
v -= 10
v -= 13
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text=ba.Lstr(resource='achievementsText'),
maxwidth=sub_width * maxwidth_scale,
)
v -= 14
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55,
text=str(data['achievementsCompleted'])
+ ' / '
+ str(len(ba.app.ach.achievements)),
maxwidth=sub_width * maxwidth_scale,
)
v -= 25
if prev_ranks_shown == 0 and showing_character:
v -= 20
elif prev_ranks_shown == 1 and showing_character:
v -= 10
center = 0.5
maxwidth_scale = 0.9
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
flatness=1.0,
text=ba.Lstr(
resource='trophiesThisSeasonText',
fallback_resource='trophiesText',
),
maxwidth=sub_width * maxwidth_scale,
)
v -= 19
ba.textwidget(
parent=self._subcontainer,
size=(0, ts_height),
position=(sub_width * 0.5, v - ts_height * tscale),
h_align='center',
v_align='top',
corner_scale=tscale,
text=trophystr,
)
except Exception:
ba.print_exception('Error displaying account info.')
def _on_cancel_press(self) -> None:
self._transition_out()
def _transition_out(self) -> None:
if not self._transitioning_out:
self._transitioning_out = True
ba.containerwidget(edit=self.root_widget, transition='out_scale')
def on_popup_cancel(self) -> None:
ba.playsound(ba.getsound('swish'))
self._transition_out()