Source code for bauiv1lib.coop.gamebutton
# Released under the MIT License. See LICENSE for details.
#
"""Defines button for co-op games."""
from __future__ import annotations
import random
import weakref
from typing import TYPE_CHECKING
import bauiv1 as bui
if TYPE_CHECKING:
from bauiv1lib.coop.browser import CoopBrowserWindow
[docs]
class GameButton:
"""Button for entering co-op games."""
def __init__(
self,
window: CoopBrowserWindow,
parent: bui.Widget,
game: str,
x: float,
y: float,
select: bool,
row: str,
):
# pylint: disable=too-many-positional-arguments
# pylint: disable=too-many-statements
# pylint: disable=too-many-locals
assert bui.app.classic is not None
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 = bui.app.classic.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._window = weakref.ref(window)
self._game = game
self._button = btn = bui.buttonwidget(
parent=parent,
position=(x + 23, y + 4),
size=(sclx, scly),
label='',
on_activate_call=self._on_press,
button_type='square',
autoselect=True,
on_select_call=bui.Call(window.sel_change, row, game),
)
bui.widget(
edit=btn,
show_buffer_bottom=50,
show_buffer_top=50,
show_buffer_left=400,
show_buffer_right=200,
)
if select:
bui.containerwidget(
edit=parent, selected_child=btn, visible_child=btn
)
image_width = sclx * 0.85 * 0.75
self._preview_widget = bui.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),
mesh_transparent=window.lsbt,
mesh_opaque=window.lsbo,
texture=bui.gettexture(
campaign.getlevel(levelname).preview_texture_name
),
mask_texture=bui.gettexture('mapPreviewMask'),
)
translated = campaign.getlevel(levelname).displayname
self._achievements = bui.app.classic.ach.achievements_for_coop_level(
game
)
self._name_widget = bui.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[bui.Widget] = []
for _i in range(stars):
imw = bui.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):
bui.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[bui.Widget, bui.Widget]] = []
for ach in self._achievements:
a_complete = ach.complete
imw = bui.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_ui_texture(a_complete),
)
imw2 = bui.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,
mesh_transparent=window.a_outline_mesh,
)
self._achievement_widgets.append((imw, imw2))
# if a_complete:
xach += a_scale * 1.2
# if not unlocked:
self._lock_widget = bui.imagewidget(
parent=parent,
draw_controller=btn,
position=(x - 8 + sclx * 0.5, y + scly * 0.5 - 20),
size=(60, 60),
opacity=0.0,
texture=bui.gettexture('lock'),
)
# give a quasi-random update increment to spread the load..
self._update_timer = bui.AppTimer(
0.001 * (900 + random.randrange(200)),
bui.WeakCall(self._update),
repeat=True,
)
self._update()
def _on_press(self) -> None:
window = self._window()
if window is not None:
window.run_game(self._game, origin_widget=self._button)
[docs]
def get_button(self) -> bui.Widget:
"""Return the underlying button bui.Widget."""
return self._button
def _update(self) -> None:
plus = bui.app.plus
assert plus is not None
classic = bui.app.classic
assert classic is not None
# 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 = classic.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.
assert bui.app.classic is not None
if not bui.app.classic.is_game_unlocked(game):
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)
)
bui.buttonwidget(
edit=self._button,
color=unlocked_color if unlocked else (0.5, 0.5, 0.5),
)
bui.imagewidget(
edit=self._lock_widget, opacity=0.0 if unlocked else 1.0
)
bui.imagewidget(
edit=self._preview_widget, opacity=1.0 if unlocked else 0.3
)
bui.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:
bui.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
bui.imagewidget(
edit=self._achievement_widgets[i][0],
opacity=1.0 if (a_complete and unlocked) else 0.3,
)
bui.imagewidget(
edit=self._achievement_widgets[i][1],
opacity=(
1.0
if (a_complete and unlocked)
else 0.2 if a_complete else 0.0
),
)
# Docs-generation hack; import some stuff that we likely only forward-declared
# in our actual source code so that docs tools can find it.
from typing import (Coroutine, Any, Literal, Callable,
Generator, Awaitable, Sequence, Self)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum