bombsquad-plugin-manager/plugins/utilities/random_play.py

208 lines
6.4 KiB
Python
Raw Normal View History

2025-01-21 22:55:59 +05:30
# ba_meta require api 9
2023-05-25 21:45:53 +02:00
from random import choice, randint
from typing import Any, Union
# pylint: disable=import-error
2023-06-28 14:05:21 +02:00
import babase
from bascenev1 import (
Session,
MultiTeamSession,
FreeForAllSession,
DualTeamSession,
GameActivity,
newactivity,
new_host_session,
)
from bauiv1 import Widget, UIScale, buttonwidget
2025-01-21 22:55:59 +05:30
from bauiv1lib.play import PlaylistSelectContext
2023-06-28 14:05:21 +02:00
from bauiv1lib.playlist.browser import PlaylistBrowserWindow
from bascenev1lib.activity.multiteamjoin import MultiTeamJoinActivity
2023-05-25 21:45:53 +02:00
DEFAULT_TEAM_COLORS = ((0.1, 0.25, 1.0), (1.0, 0.25, 0.2))
DEFAULT_TEAM_NAMES = ("Blue", "Red")
# More or less copied from game code
# I have no idea what I'm doing here
2023-06-28 14:05:21 +02:00
class RandomPlaySessionMixin(MultiTeamSession, Session):
2023-05-25 21:45:53 +02:00
def __init__(self, playlist) -> None:
2023-06-28 14:05:21 +02:00
"""Set up playlists & launch a bascenev1.Activity to accept joiners."""
2023-05-25 21:45:53 +02:00
2023-06-28 14:05:21 +02:00
app = babase.app
classic = app.classic
assert classic is not None
2023-05-25 21:45:53 +02:00
_cfg = app.config
2023-06-28 14:05:21 +02:00
super(MultiTeamSession, self).__init__(
2023-05-25 21:45:53 +02:00
[],
team_names=DEFAULT_TEAM_NAMES,
team_colors=DEFAULT_TEAM_COLORS,
min_players=1,
max_players=self.get_max_players(),
)
2023-06-28 14:05:21 +02:00
self._series_length: int = classic.teams_series_length
self._ffa_series_length: int = classic.ffa_series_length
2023-05-25 21:45:53 +02:00
self._tutorial_activity_instance = None
self._game_number = 0
# Our faux playlist
self._playlist = playlist
self._current_game_spec: dict[str, Any] | None = None
self._next_game_spec: dict[str, Any] = self._playlist.pull_next()
2023-06-28 14:05:21 +02:00
self._next_game: type[GameActivity] = self._next_game_spec["resolved_type"]
2023-05-25 21:45:53 +02:00
self._instantiate_next_game()
2023-06-28 14:05:21 +02:00
self.setactivity(newactivity(MultiTeamJoinActivity))
2023-05-25 21:45:53 +02:00
# Classes for Teams autopilot and FFA autopilot
# I think they have to be separate in order to comply with `ba.GameActivity.supports_session_type()`
2023-06-28 14:05:21 +02:00
class RandFreeForAllSession(FreeForAllSession, RandomPlaySessionMixin):
2023-05-25 21:45:53 +02:00
def __init__(self):
2023-06-28 14:05:21 +02:00
playlist = RandomPlaylist(FreeForAllSession)
super(FreeForAllSession, self).__init__(playlist)
2023-05-25 21:45:53 +02:00
2023-06-28 14:05:21 +02:00
class RandDualTeamSession(DualTeamSession, RandomPlaySessionMixin):
2023-05-25 21:45:53 +02:00
def __init__(self):
2023-06-28 14:05:21 +02:00
playlist = RandomPlaylist(DualTeamSession)
super(DualTeamSession, self).__init__(playlist)
2023-05-25 21:45:53 +02:00
# The faux playlist that just picks games at random
class RandomPlaylist:
2023-06-28 14:05:21 +02:00
sessiontype: Session
all_games: list[GameActivity]
usable_games: list[GameActivity]
2023-05-25 21:45:53 +02:00
2023-05-25 22:29:27 +02:00
last_game: str
2023-05-25 21:45:53 +02:00
def __init__(self, sessiontype):
self.sessiontype = sessiontype
2023-06-28 14:05:21 +02:00
self.usable_games: list[GameActivity] = [
2023-05-25 21:45:53 +02:00
gt
for gt in RandomPlaylist.all_games
if gt.supports_session_type(self.sessiontype)
]
self.last_game = None
def pull_next(self) -> dict[str, Any]:
"""
Generate a new game at random.
"""
has_only_one_game = len(self.usable_games) == 1
while True:
game = choice(self.usable_games)
2023-05-25 22:29:27 +02:00
if game.name == self.last_game:
2023-05-25 21:45:53 +02:00
# Don't repeat the same game twice
if has_only_one_game:
2023-06-28 14:05:21 +02:00
# ...but don't freeze when there's only one game
2023-05-25 21:45:53 +02:00
break
else:
break
2023-05-25 22:29:27 +02:00
self.last_game = game.name
2023-05-25 21:45:53 +02:00
game_map = choice(game.get_supported_maps(self.sessiontype))
settings = {
s.name: s.default for s in game.get_available_settings(self.sessiontype)
}
settings["map"] = game_map
if "Epic Mode" in settings:
# Throw in an Epic Mode once in a while
settings["Epic Mode"] = randint(0, 4) == 4
return {"resolved_type": game, "settings": settings}
# Adapted from plugin quick_custom_game.
# Hope you don't mind.
def patched__init__(
self,
2023-06-28 14:05:21 +02:00
sessiontype: type[Session],
2023-05-25 21:45:53 +02:00
transition: str | None = "in_right",
2023-06-28 14:05:21 +02:00
origin_widget: Widget | None = None,
2025-01-21 22:55:59 +05:30
playlist_select_context: PlaylistSelectContext | None = None,
2023-05-25 21:45:53 +02:00
):
width = 800
height = 650
2023-06-28 14:05:21 +02:00
ui_scale = babase.app.ui_v1.uiscale
2025-01-21 22:55:59 +05:30
y_offset = -95 if ui_scale is UIScale.SMALL else -35 if ui_scale is UIScale.MEDIUM else 115
x_offset = 140 if ui_scale is UIScale.SMALL else 80 if ui_scale is UIScale.MEDIUM else 240
2023-05-25 21:45:53 +02:00
self.old__init__(sessiontype, transition, origin_widget)
# pylint: disable=protected-access
2023-06-28 14:05:21 +02:00
self._quick_game_button = buttonwidget(
2023-05-25 21:45:53 +02:00
parent=self._root_widget,
position=(width - 120 * 2 + x_offset, height - 132 + y_offset),
autoselect=True,
2025-01-21 22:55:59 +05:30
size=(80, 50),
scale=0.6 if ui_scale is UIScale.SMALL else 1.15,
2023-05-25 21:45:53 +02:00
text_scale=1.2,
2023-06-28 14:05:21 +02:00
label="Random",
2023-05-25 21:45:53 +02:00
on_activate_call=game_starter_factory(sessiontype),
color=(0.54, 0.52, 0.67),
textcolor=(0.7, 0.65, 0.7),
)
2023-06-28 12:08:31 +00:00
2023-05-25 21:45:53 +02:00
# Returns a function that starts the game
2023-06-28 12:08:31 +00:00
2023-06-28 14:05:21 +02:00
def game_starter_factory(sessiontype: type[Session]):
2023-05-25 21:45:53 +02:00
session: Union[RandFreeForAllSession, RandDualTeamSession] = None
2023-06-28 14:05:21 +02:00
if issubclass(sessiontype, FreeForAllSession):
2023-05-25 21:45:53 +02:00
session = RandFreeForAllSession
2023-06-28 14:05:21 +02:00
elif issubclass(sessiontype, DualTeamSession):
2023-05-25 21:45:53 +02:00
session = RandDualTeamSession
else:
raise RuntimeError("Can't determine session type")
def on_run():
can_start = False
def do_start(game_list):
nonlocal can_start
RandomPlaylist.all_games = game_list
if not can_start: # Don't start if the screen fade is still ongoing
can_start = True
else:
start()
def has_faded():
nonlocal can_start
if not can_start: # Don't start if it's still loading
can_start = True
else:
start()
def start():
2023-06-28 14:05:21 +02:00
babase.unlock_all_input()
new_host_session(session)
2023-05-25 21:45:53 +02:00
2023-06-28 14:05:21 +02:00
babase.fade_screen(False, time=0.25, endcall=has_faded)
babase.lock_all_input()
babase.app.meta.load_exported_classes(GameActivity, do_start)
2023-05-25 21:45:53 +02:00
return on_run
2025-06-24 00:55:09 +05:30
# ba_meta export babase.Plugin
2023-06-28 14:05:21 +02:00
class RandomPlayPlugin(babase.Plugin):
2023-05-25 21:45:53 +02:00
"""
A plugin that allows you to play randomly generated FFA or Teams matches by selecting a random minigame and map for each round.
This eliminates the need to set up long playlists to enjoy all your BombSquad content.
"""
def __init__(self):
PlaylistBrowserWindow.old__init__ = PlaylistBrowserWindow.__init__
PlaylistBrowserWindow.__init__ = patched__init__