mirror of
https://github.com/hypervortex/VH-Bombsquad-Modded-Server-Files
synced 2025-11-07 17:36:08 +00:00
303 lines
9.5 KiB
Python
303 lines
9.5 KiB
Python
# Released under the MIT License. See LICENSE for details.
|
|
#
|
|
"""Defines button for co-op games."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import random
|
|
from typing import TYPE_CHECKING
|
|
|
|
import ba
|
|
|
|
if TYPE_CHECKING:
|
|
from bastd.ui.coop.browser import CoopBrowserWindow
|
|
|
|
|
|
class GameButton:
|
|
"""Button for entering co-op games."""
|
|
|
|
def __init__(
|
|
self,
|
|
window: CoopBrowserWindow,
|
|
parent: ba.Widget,
|
|
game: str,
|
|
x: float,
|
|
y: float,
|
|
select: bool,
|
|
row: str,
|
|
):
|
|
# pylint: disable=too-many-statements
|
|
# pylint: disable=too-many-locals
|
|
from ba.internal import getcampaign
|
|
|
|
self._game = game
|
|
sclx = 195.0
|
|
scly = 195.0
|
|
|
|
campaignname, levelname = game.split(':')
|
|
|
|
# Hack: The Last Stand doesn't actually exist in the easy
|
|
# tourney. We just want it for display purposes. Map it to
|
|
# the hard-mode version.
|
|
if game == 'Easy:The Last Stand':
|
|
campaignname = 'Default'
|
|
|
|
rating: float | None
|
|
campaign = getcampaign(campaignname)
|
|
rating = campaign.getlevel(levelname).rating
|
|
|
|
if game == 'Easy:The Last Stand':
|
|
rating = None
|
|
|
|
if rating is None or rating == 0.0:
|
|
stars = 0
|
|
elif rating >= 9.5:
|
|
stars = 3
|
|
elif rating >= 7.5:
|
|
stars = 2
|
|
else:
|
|
stars = 1
|
|
|
|
self._button = btn = ba.buttonwidget(
|
|
parent=parent,
|
|
position=(x + 23, y + 4),
|
|
size=(sclx, scly),
|
|
label='',
|
|
on_activate_call=ba.Call(window.run_game, game),
|
|
button_type='square',
|
|
autoselect=True,
|
|
on_select_call=ba.Call(window.sel_change, row, game),
|
|
)
|
|
ba.widget(
|
|
edit=btn,
|
|
show_buffer_bottom=50,
|
|
show_buffer_top=50,
|
|
show_buffer_left=400,
|
|
show_buffer_right=200,
|
|
)
|
|
if select:
|
|
ba.containerwidget(
|
|
edit=parent, selected_child=btn, visible_child=btn
|
|
)
|
|
image_width = sclx * 0.85 * 0.75
|
|
self._preview_widget = ba.imagewidget(
|
|
parent=parent,
|
|
draw_controller=btn,
|
|
position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 104),
|
|
size=(image_width, image_width * 0.5),
|
|
model_transparent=window.lsbt,
|
|
model_opaque=window.lsbo,
|
|
texture=campaign.getlevel(levelname).get_preview_texture(),
|
|
mask_texture=ba.gettexture('mapPreviewMask'),
|
|
)
|
|
|
|
translated = campaign.getlevel(levelname).displayname
|
|
self._achievements = ba.app.ach.achievements_for_coop_level(game)
|
|
|
|
self._name_widget = ba.textwidget(
|
|
parent=parent,
|
|
draw_controller=btn,
|
|
position=(x + 20 + sclx * 0.5, y + scly - 27),
|
|
size=(0, 0),
|
|
h_align='center',
|
|
text=translated,
|
|
v_align='center',
|
|
maxwidth=sclx * 0.76,
|
|
scale=0.85,
|
|
)
|
|
xscl = x + (67 if self._achievements else 50)
|
|
yscl = y + scly - (137 if self._achievements else 157)
|
|
|
|
starscale = 35.0 if self._achievements else 45.0
|
|
|
|
self._star_widgets: list[ba.Widget] = []
|
|
for _i in range(stars):
|
|
imw = ba.imagewidget(
|
|
parent=parent,
|
|
draw_controller=btn,
|
|
position=(xscl, yscl),
|
|
size=(starscale, starscale),
|
|
texture=window.star_tex,
|
|
)
|
|
self._star_widgets.append(imw)
|
|
xscl += starscale
|
|
for _i in range(3 - stars):
|
|
ba.imagewidget(
|
|
parent=parent,
|
|
draw_controller=btn,
|
|
position=(xscl, yscl),
|
|
size=(starscale, starscale),
|
|
color=(0, 0, 0),
|
|
texture=window.star_tex,
|
|
opacity=0.3,
|
|
)
|
|
xscl += starscale
|
|
|
|
xach = x + 69
|
|
yach = y + scly - 168
|
|
a_scale = 30.0
|
|
self._achievement_widgets: list[tuple[ba.Widget, ba.Widget]] = []
|
|
for ach in self._achievements:
|
|
a_complete = ach.complete
|
|
imw = ba.imagewidget(
|
|
parent=parent,
|
|
draw_controller=btn,
|
|
position=(xach, yach),
|
|
size=(a_scale, a_scale),
|
|
color=tuple(ach.get_icon_color(a_complete)[:3])
|
|
if a_complete
|
|
else (1.2, 1.2, 1.2),
|
|
texture=ach.get_icon_texture(a_complete),
|
|
)
|
|
imw2 = ba.imagewidget(
|
|
parent=parent,
|
|
draw_controller=btn,
|
|
position=(xach, yach),
|
|
size=(a_scale, a_scale),
|
|
color=(2, 1.4, 0.4),
|
|
texture=window.a_outline_tex,
|
|
model_transparent=window.a_outline_model,
|
|
)
|
|
self._achievement_widgets.append((imw, imw2))
|
|
# if a_complete:
|
|
xach += a_scale * 1.2
|
|
|
|
# if not unlocked:
|
|
self._lock_widget = ba.imagewidget(
|
|
parent=parent,
|
|
draw_controller=btn,
|
|
position=(x - 8 + sclx * 0.5, y + scly * 0.5 - 20),
|
|
size=(60, 60),
|
|
opacity=0.0,
|
|
texture=ba.gettexture('lock'),
|
|
)
|
|
|
|
# give a quasi-random update increment to spread the load..
|
|
self._update_timer = ba.Timer(
|
|
0.001 * (900 + random.randrange(200)),
|
|
ba.WeakCall(self._update),
|
|
repeat=True,
|
|
timetype=ba.TimeType.REAL,
|
|
)
|
|
self._update()
|
|
|
|
def get_button(self) -> ba.Widget:
|
|
"""Return the underlying button ba.Widget."""
|
|
return self._button
|
|
|
|
def _update(self) -> None:
|
|
# pylint: disable=too-many-boolean-expressions
|
|
from ba.internal import getcampaign
|
|
|
|
# In case we stick around after our UI...
|
|
if not self._button:
|
|
return
|
|
|
|
game = self._game
|
|
campaignname, levelname = game.split(':')
|
|
|
|
# Hack - The Last Stand doesn't actually exist in the
|
|
# easy tourney; we just want it for display purposes. Map it to
|
|
# the hard-mode version.
|
|
if game == 'Easy:The Last Stand':
|
|
campaignname = 'Default'
|
|
|
|
campaign = getcampaign(campaignname)
|
|
|
|
# If this campaign is sequential, make sure we've unlocked
|
|
# everything up to here.
|
|
unlocked = True
|
|
if campaign.sequential:
|
|
for level in campaign.levels:
|
|
if level.name == levelname:
|
|
break
|
|
if not level.complete:
|
|
unlocked = False
|
|
break
|
|
|
|
# We never actually allow playing last-stand on easy mode.
|
|
if game == 'Easy:The Last Stand':
|
|
unlocked = False
|
|
|
|
# Hard-code games we haven't unlocked.
|
|
if (
|
|
(
|
|
game
|
|
in (
|
|
'Challenges:Infinite Runaround',
|
|
'Challenges:Infinite Onslaught',
|
|
)
|
|
and not ba.app.accounts_v1.have_pro()
|
|
)
|
|
or (
|
|
game in ('Challenges:Meteor Shower',)
|
|
and not ba.internal.get_purchased('games.meteor_shower')
|
|
)
|
|
or (
|
|
game
|
|
in (
|
|
'Challenges:Target Practice',
|
|
'Challenges:Target Practice B',
|
|
)
|
|
and not ba.internal.get_purchased('games.target_practice')
|
|
)
|
|
or (
|
|
game in ('Challenges:Ninja Fight',)
|
|
and not ba.internal.get_purchased('games.ninja_fight')
|
|
)
|
|
or (
|
|
game in ('Challenges:Pro Ninja Fight',)
|
|
and not ba.internal.get_purchased('games.ninja_fight')
|
|
)
|
|
or (
|
|
game
|
|
in (
|
|
'Challenges:Easter Egg Hunt',
|
|
'Challenges:Pro Easter Egg Hunt',
|
|
)
|
|
and not ba.internal.get_purchased('games.easter_egg_hunt')
|
|
)
|
|
):
|
|
unlocked = False
|
|
|
|
# Let's tint levels a slightly different color when easy mode
|
|
# is selected.
|
|
unlocked_color = (
|
|
(0.85, 0.95, 0.5) if game.startswith('Easy:') else (0.5, 0.7, 0.2)
|
|
)
|
|
|
|
ba.buttonwidget(
|
|
edit=self._button,
|
|
color=unlocked_color if unlocked else (0.5, 0.5, 0.5),
|
|
)
|
|
|
|
ba.imagewidget(edit=self._lock_widget, opacity=0.0 if unlocked else 1.0)
|
|
ba.imagewidget(
|
|
edit=self._preview_widget, opacity=1.0 if unlocked else 0.3
|
|
)
|
|
ba.textwidget(
|
|
edit=self._name_widget,
|
|
color=(0.8, 1.0, 0.8, 1.0) if unlocked else (0.7, 0.7, 0.7, 0.7),
|
|
)
|
|
for widget in self._star_widgets:
|
|
ba.imagewidget(
|
|
edit=widget,
|
|
opacity=1.0 if unlocked else 0.3,
|
|
color=(2.2, 1.2, 0.3) if unlocked else (1, 1, 1),
|
|
)
|
|
for i, ach in enumerate(self._achievements):
|
|
a_complete = ach.complete
|
|
ba.imagewidget(
|
|
edit=self._achievement_widgets[i][0],
|
|
opacity=1.0 if (a_complete and unlocked) else 0.3,
|
|
)
|
|
ba.imagewidget(
|
|
edit=self._achievement_widgets[i][1],
|
|
opacity=(
|
|
1.0
|
|
if (a_complete and unlocked)
|
|
else 0.2
|
|
if a_complete
|
|
else 0.0
|
|
),
|
|
)
|