ba_data update

This commit is contained in:
Ayush Saini 2024-01-27 21:25:16 +05:30
parent 2174ae566d
commit 7ba24ecbcf
146 changed files with 1756 additions and 347 deletions

View file

@ -48,6 +48,7 @@ from _babase import (
fatal_error,
get_display_resolution,
get_immediate_return_code,
get_input_idle_time,
get_low_level_config_value,
get_max_graphics_quality,
get_replays_dir,
@ -60,6 +61,7 @@ from _babase import (
have_permission,
in_logic_thread,
increment_analytics_count,
invoke_main_menu,
is_os_playing_music,
is_xcode_build,
lock_all_input,
@ -116,7 +118,6 @@ from babase._apputils import (
get_remote_app_name,
AppHealthMonitor,
)
from babase._cloud import CloudSubsystem
from babase._devconsole import (
DevConsoleTab,
DevConsoleTabEntry,
@ -211,7 +212,6 @@ __all__ = [
'clipboard_has_text',
'clipboard_is_supported',
'clipboard_set_text',
'CloudSubsystem',
'commit_app_config',
'ContextCall',
'ContextError',
@ -235,6 +235,7 @@ __all__ = [
'garbage_collect',
'get_display_resolution',
'get_immediate_return_code',
'get_input_idle_time',
'get_ip_address_type',
'get_low_level_config_value',
'get_max_graphics_quality',
@ -254,6 +255,7 @@ __all__ = [
'increment_analytics_count',
'InputDeviceNotFoundError',
'InputType',
'invoke_main_menu',
'is_browser_likely_available',
'is_browser_likely_available',
'is_os_playing_music',

View file

@ -1,15 +1,17 @@
# Released under the MIT License. See LICENSE for details.
#
# pylint: disable=too-many-lines
"""Functionality related to the high level state of the app."""
from __future__ import annotations
import os
import logging
from enum import Enum
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, TypeVar
from concurrent.futures import ThreadPoolExecutor
from functools import cached_property
from typing_extensions import override
from efro.call import tpartial
import _babase
@ -26,7 +28,7 @@ from babase._devconsole import DevConsoleSubsystem
if TYPE_CHECKING:
import asyncio
from typing import Any, Callable, Coroutine
from typing import Any, Callable, Coroutine, Generator, Awaitable
from concurrent.futures import Future
import babase
@ -42,6 +44,8 @@ if TYPE_CHECKING:
# __FEATURESET_APP_SUBSYSTEM_IMPORTS_END__
T = TypeVar('T')
class App:
"""A class for high level app functionality and state.
@ -124,6 +128,7 @@ class App:
statically in a spinoff project.
"""
@override
def app_mode_for_intent(
self, intent: AppIntent
) -> type[AppMode] | None:
@ -199,7 +204,8 @@ class App:
self._called_on_running = False
self._subsystem_registration_ended = False
self._pending_apply_app_config = False
self._aioloop: asyncio.AbstractEventLoop | None = None
self._asyncio_loop: asyncio.AbstractEventLoop | None = None
self._asyncio_tasks: set[asyncio.Task] = set()
self._asyncio_timer: babase.AppTimer | None = None
self._config: babase.AppConfig | None = None
self._pending_intent: AppIntent | None = None
@ -239,18 +245,68 @@ class App:
return _babase.app_is_active()
@property
def aioloop(self) -> asyncio.AbstractEventLoop:
def asyncio_loop(self) -> asyncio.AbstractEventLoop:
"""The logic thread's asyncio event loop.
This allow async tasks to be run in the logic thread.
Generally you should call App.create_async_task() to schedule
async code to run instead of using this directly. That will
handle retaining the task and logging errors automatically.
Only schedule tasks onto asyncio_loop yourself when you intend
to hold on to the returned task and await its results. Releasing
the task reference can lead to subtle bugs such as unreported
errors and garbage-collected tasks disappearing before their
work is done.
Note that, at this time, the asyncio loop is encapsulated
and explicitly stepped by the engine's logic thread loop and
thus things like asyncio.get_running_loop() will not return this
loop from most places in the logic thread; only from within a
task explicitly created in this loop.
thus things like asyncio.get_running_loop() will unintuitively
*not* return this loop from most places in the logic thread;
only from within a task explicitly created in this loop.
Hopefully this situation will be improved in the future with a
unified event loop.
"""
assert self._aioloop is not None
return self._aioloop
assert _babase.in_logic_thread()
assert self._asyncio_loop is not None
return self._asyncio_loop
def create_async_task(
self,
coro: Generator[Any, Any, T] | Coroutine[Any, Any, T],
*,
name: str | None = None,
) -> None:
"""Create a fully managed async task.
This will automatically retain and release a reference to the task
and log any exceptions that occur in it. If you need to await a task
or otherwise need more control, schedule a task directly using
App.asyncio_loop.
"""
assert _babase.in_logic_thread()
# Hold a strong reference to the task until it is done.
# Otherwise it is possible for it to be garbage collected and
# disappear midway if the caller does not hold on to the
# returned task, which seems like a great way to introduce
# hard-to-track bugs.
task = self.asyncio_loop.create_task(coro, name=name)
self._asyncio_tasks.add(task)
task.add_done_callback(self._on_task_done)
# return task
def _on_task_done(self, task: asyncio.Task) -> None:
# Report any errors that occurred.
try:
exc = task.exception()
if exc is not None:
logging.error(
"Error in async task '%s'.", task.get_name(), exc_info=exc
)
except Exception:
logging.exception('Error reporting async task error.')
self._asyncio_tasks.remove(task)
@property
def config(self) -> babase.AppConfig:
@ -437,6 +493,12 @@ class App:
self._native_shutdown_complete_called = True
self._update_state()
def on_native_active_changed(self) -> None:
"""Called by the native layer when the app active state changes."""
assert _babase.in_logic_thread()
if self._mode is not None:
self._mode.on_app_active_changed()
def read_config(self) -> None:
"""(internal)"""
from babase._appconfig import read_app_config
@ -588,7 +650,7 @@ class App:
_env.on_app_state_initing()
self._aioloop = _asyncio.setup_asyncio()
self._asyncio_loop = _asyncio.setup_asyncio()
self.health_monitor = AppHealthMonitor()
# __FEATURESET_APP_SUBSYSTEM_CREATE_BEGIN__
@ -868,8 +930,8 @@ class App:
)
# Now kick off any async shutdown task(s).
assert self._aioloop is not None
self._shutdown_task = self._aioloop.create_task(self._shutdown())
assert self._asyncio_loop is not None
self._shutdown_task = self._asyncio_loop.create_task(self._shutdown())
def _on_shutdown_complete(self) -> None:
"""(internal)"""

View file

@ -52,3 +52,10 @@ class AppMode:
def on_deactivate(self) -> None:
"""Called when the mode is being deactivated."""
def on_app_active_changed(self) -> None:
"""Called when babase.app.active changes.
The app-mode may want to take action such as pausing a running
game in such cases.
"""

View file

@ -40,16 +40,16 @@ class AppSubsystem:
"""Called when the app reaches the running state."""
def on_app_suspend(self) -> None:
"""Called when the app enters the paused state."""
"""Called when the app enters the suspended state."""
def on_app_unsuspend(self) -> None:
"""Called when the app exits the paused state."""
"""Called when the app exits the suspended state."""
def on_app_shutdown(self) -> None:
"""Called when the app is shutting down."""
"""Called when the app begins shutting down."""
def on_app_shutdown_complete(self) -> None:
"""Called when the app is done shutting down."""
"""Called when the app completes shutting down."""
def do_apply_app_config(self) -> None:
"""Called when the app config should be applied."""

View file

@ -10,9 +10,11 @@ from threading import Thread
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing_extensions import override
from efro.call import tpartial
from efro.log import LogLevel
from efro.dataclassio import ioprepped, dataclass_to_json, dataclass_from_json
import _babase
from babase._appsubsystem import AppSubsystem
@ -386,6 +388,7 @@ class AppHealthMonitor(AppSubsystem):
self._response = False
self._first_check = True
@override
def on_app_loading(self) -> None:
# If any traceback dumps happened last run, log and clear them.
log_dumped_app_state(from_previous_run=True)
@ -449,10 +452,12 @@ class AppHealthMonitor(AppSubsystem):
self._first_check = False
@override
def on_app_suspend(self) -> None:
assert _babase.in_logic_thread()
self._running = False
@override
def on_app_unsuspend(self) -> None:
assert _babase.in_logic_thread()
self._running = True

View file

@ -8,6 +8,8 @@ from typing import TYPE_CHECKING
from dataclasses import dataclass
import logging
from typing_extensions import override
import _babase
if TYPE_CHECKING:
@ -96,6 +98,7 @@ class DevConsoleTab:
class DevConsoleTabPython(DevConsoleTab):
"""The Python dev-console tab."""
@override
def refresh(self) -> None:
self.python_terminal()
@ -103,6 +106,7 @@ class DevConsoleTabPython(DevConsoleTab):
class DevConsoleTabTest(DevConsoleTab):
"""Test dev-console tab."""
@override
def refresh(self) -> None:
import random

View file

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from bacommon.app import AppExperience
import _babase
@ -18,15 +19,18 @@ if TYPE_CHECKING:
class EmptyAppMode(AppMode):
"""An empty app mode that can be used as a fallback/etc."""
@override
@classmethod
def get_app_experience(cls) -> AppExperience:
return AppExperience.EMPTY
@override
@classmethod
def _supports_intent(cls, intent: AppIntent) -> bool:
# We support default and exec intents currently.
return isinstance(intent, AppIntentExec | AppIntentDefault)
@override
def handle_intent(self, intent: AppIntent) -> None:
if isinstance(intent, AppIntentExec):
_babase.empty_app_mode_handle_intent_exec(intent.code)
@ -34,10 +38,12 @@ class EmptyAppMode(AppMode):
assert isinstance(intent, AppIntentDefault)
_babase.empty_app_mode_handle_intent_default()
@override
def on_activate(self) -> None:
# Let the native layer do its thing.
_babase.on_empty_app_mode_activate()
@override
def on_deactivate(self) -> None:
# Let the native layer do its thing.
_babase.on_empty_app_mode_deactivate()

View file

@ -9,6 +9,7 @@ import logging
import warnings
from typing import TYPE_CHECKING
from typing_extensions import override
from efro.log import LogLevel
if TYPE_CHECKING:
@ -216,6 +217,7 @@ def _feed_logs_to_babase(log_handler: LogHandler) -> None:
class _CustomHelper:
"""Replacement 'help' that behaves better for our setup."""
@override
def __repr__(self) -> str:
return 'Type help(object) for help about object.'

View file

@ -10,6 +10,7 @@ import logging
import inspect
from typing import TYPE_CHECKING, TypeVar, Protocol, NewType
from typing_extensions import override
from efro.terminal import Clr
import _babase
@ -178,6 +179,7 @@ class _WeakCall:
def __call__(self, *args_extra: Any) -> Any:
return self._call(*self._args + args_extra, **self._keywds)
@override
def __str__(self) -> str:
return (
'<ba.WeakCall object; _call='
@ -224,6 +226,7 @@ class _Call:
def __call__(self, *args_extra: Any) -> Any:
return self._call(*self._args + args_extra, **self._keywds)
@override
def __str__(self) -> str:
return (
'<ba.Call object; _call='
@ -268,6 +271,7 @@ class WeakMethod:
return None
return self._func(*((obj,) + args), **keywds)
@override
def __str__(self) -> str:
return '<ba.WeakMethod object; call=' + str(self._func) + '>'

View file

@ -8,6 +8,8 @@ import json
import logging
from typing import TYPE_CHECKING, overload
from typing_extensions import override
import _babase
from babase._appsubsystem import AppSubsystem
@ -217,6 +219,7 @@ class LanguageSubsystem(AppSubsystem):
color=(0, 1, 0),
)
@override
def do_apply_app_config(self) -> None:
assert _babase.in_logic_thread()
assert isinstance(_babase.app.config, dict)
@ -598,9 +601,11 @@ class Lstr:
_error.print_exception('_get_json failed for', self.args)
return 'JSON_ERR'
@override
def __str__(self) -> str:
return '<ba.Lstr: ' + self._get_json() + '>'
@override
def __repr__(self) -> str:
return '<ba.Lstr: ' + self._get_json() + '>'
@ -648,5 +653,6 @@ class AttrDict(dict):
assert not isinstance(val, bytes)
return val
@override
def __setattr__(self, attr: str, value: Any) -> None:
raise AttributeError()

View file

@ -9,6 +9,7 @@ import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING, final
from typing_extensions import override
from bacommon.login import LoginType
import _babase
@ -353,6 +354,7 @@ class LoginAdapterNative(LoginAdapter):
self._sign_in_attempt_num = 123
self._sign_in_attempts: dict[int, Callable[[str | None], None]] = {}
@override
def get_sign_in_token(
self, completion_cb: Callable[[str | None], None]
) -> None:
@ -363,6 +365,7 @@ class LoginAdapterNative(LoginAdapter):
self.login_type.value, attempt_id
)
@override
def on_back_end_active_change(self, active: bool) -> None:
_babase.login_adapter_back_end_active_change(
self.login_type.value, active

View file

@ -26,7 +26,7 @@ EXPORT_CLASS_NAME_SHORTCUTS: dict[str, str] = {
'plugin': 'babase.Plugin',
# DEPRECATED as of 12/2023. Currently am warning if finding these
# but should take this out eventually.
'keyboard': 'babase.Keyboard',
'keyboard': 'bauiv1.Keyboard',
}
T = TypeVar('T')

View file

@ -149,78 +149,84 @@ class SpecialChar(Enum):
PLAY_PAUSE_BUTTON = 13
FAST_FORWARD_BUTTON = 14
DPAD_CENTER_BUTTON = 15
OUYA_BUTTON_O = 16
OUYA_BUTTON_U = 17
OUYA_BUTTON_Y = 18
OUYA_BUTTON_A = 19
OUYA_LOGO = 20
LOGO = 21
TICKET = 22
GOOGLE_PLAY_GAMES_LOGO = 23
GAME_CENTER_LOGO = 24
DICE_BUTTON1 = 25
DICE_BUTTON2 = 26
DICE_BUTTON3 = 27
DICE_BUTTON4 = 28
GAME_CIRCLE_LOGO = 29
PARTY_ICON = 30
TEST_ACCOUNT = 31
TICKET_BACKING = 32
TROPHY1 = 33
TROPHY2 = 34
TROPHY3 = 35
TROPHY0A = 36
TROPHY0B = 37
TROPHY4 = 38
LOCAL_ACCOUNT = 39
EXPLODINARY_LOGO = 40
FLAG_UNITED_STATES = 41
FLAG_MEXICO = 42
FLAG_GERMANY = 43
FLAG_BRAZIL = 44
FLAG_RUSSIA = 45
FLAG_CHINA = 46
FLAG_UNITED_KINGDOM = 47
FLAG_CANADA = 48
FLAG_INDIA = 49
FLAG_JAPAN = 50
FLAG_FRANCE = 51
FLAG_INDONESIA = 52
FLAG_ITALY = 53
FLAG_SOUTH_KOREA = 54
FLAG_NETHERLANDS = 55
FEDORA = 56
HAL = 57
CROWN = 58
YIN_YANG = 59
EYE_BALL = 60
SKULL = 61
HEART = 62
DRAGON = 63
HELMET = 64
MUSHROOM = 65
NINJA_STAR = 66
VIKING_HELMET = 67
MOON = 68
SPIDER = 69
FIREBALL = 70
FLAG_UNITED_ARAB_EMIRATES = 71
FLAG_QATAR = 72
FLAG_EGYPT = 73
FLAG_KUWAIT = 74
FLAG_ALGERIA = 75
FLAG_SAUDI_ARABIA = 76
FLAG_MALAYSIA = 77
FLAG_CZECH_REPUBLIC = 78
FLAG_AUSTRALIA = 79
FLAG_SINGAPORE = 80
OCULUS_LOGO = 81
STEAM_LOGO = 82
NVIDIA_LOGO = 83
FLAG_IRAN = 84
FLAG_POLAND = 85
FLAG_ARGENTINA = 86
FLAG_PHILIPPINES = 87
FLAG_CHILE = 88
MIKIROG = 89
V2_LOGO = 90
PLAY_STATION_CROSS_BUTTON = 16
PLAY_STATION_CIRCLE_BUTTON = 17
PLAY_STATION_TRIANGLE_BUTTON = 18
PLAY_STATION_SQUARE_BUTTON = 19
PLAY_BUTTON = 20
PAUSE_BUTTON = 21
OUYA_BUTTON_O = 22
OUYA_BUTTON_U = 23
OUYA_BUTTON_Y = 24
OUYA_BUTTON_A = 25
OUYA_LOGO = 26
LOGO = 27
TICKET = 28
GOOGLE_PLAY_GAMES_LOGO = 29
GAME_CENTER_LOGO = 30
DICE_BUTTON1 = 31
DICE_BUTTON2 = 32
DICE_BUTTON3 = 33
DICE_BUTTON4 = 34
GAME_CIRCLE_LOGO = 35
PARTY_ICON = 36
TEST_ACCOUNT = 37
TICKET_BACKING = 38
TROPHY1 = 39
TROPHY2 = 40
TROPHY3 = 41
TROPHY0A = 42
TROPHY0B = 43
TROPHY4 = 44
LOCAL_ACCOUNT = 45
EXPLODINARY_LOGO = 46
FLAG_UNITED_STATES = 47
FLAG_MEXICO = 48
FLAG_GERMANY = 49
FLAG_BRAZIL = 50
FLAG_RUSSIA = 51
FLAG_CHINA = 52
FLAG_UNITED_KINGDOM = 53
FLAG_CANADA = 54
FLAG_INDIA = 55
FLAG_JAPAN = 56
FLAG_FRANCE = 57
FLAG_INDONESIA = 58
FLAG_ITALY = 59
FLAG_SOUTH_KOREA = 60
FLAG_NETHERLANDS = 61
FEDORA = 62
HAL = 63
CROWN = 64
YIN_YANG = 65
EYE_BALL = 66
SKULL = 67
HEART = 68
DRAGON = 69
HELMET = 70
MUSHROOM = 71
NINJA_STAR = 72
VIKING_HELMET = 73
MOON = 74
SPIDER = 75
FIREBALL = 76
FLAG_UNITED_ARAB_EMIRATES = 77
FLAG_QATAR = 78
FLAG_EGYPT = 79
FLAG_KUWAIT = 80
FLAG_ALGERIA = 81
FLAG_SAUDI_ARABIA = 82
FLAG_MALAYSIA = 83
FLAG_CZECH_REPUBLIC = 84
FLAG_AUSTRALIA = 85
FLAG_SINGAPORE = 86
OCULUS_LOGO = 87
STEAM_LOGO = 88
NVIDIA_LOGO = 89
FLAG_IRAN = 90
FLAG_POLAND = 91
FLAG_ARGENTINA = 92
FLAG_PHILIPPINES = 93
FLAG_CHILE = 94
MIKIROG = 95
V2_LOGO = 96

View file

@ -8,6 +8,8 @@ import logging
import importlib.util
from typing import TYPE_CHECKING
from typing_extensions import override
import _babase
from babase._appsubsystem import AppSubsystem
@ -158,6 +160,7 @@ class PluginSubsystem(AppSubsystem):
if config_changed:
_babase.app.config.commit()
@override
def on_app_running(self) -> None:
# Load up our plugins and go ahead and call their on_app_running
# calls.
@ -170,6 +173,7 @@ class PluginSubsystem(AppSubsystem):
_error.print_exception('Error in plugin on_app_running()')
@override
def on_app_suspend(self) -> None:
for plugin in self.active_plugins:
try:
@ -179,6 +183,7 @@ class PluginSubsystem(AppSubsystem):
_error.print_exception('Error in plugin on_app_suspend()')
@override
def on_app_unsuspend(self) -> None:
for plugin in self.active_plugins:
try:
@ -188,6 +193,7 @@ class PluginSubsystem(AppSubsystem):
_error.print_exception('Error in plugin on_app_unsuspend()')
@override
def on_app_shutdown(self) -> None:
for plugin in self.active_plugins:
try:
@ -197,6 +203,7 @@ class PluginSubsystem(AppSubsystem):
_error.print_exception('Error in plugin on_app_shutdown()')
@override
def on_app_shutdown_complete(self) -> None:
for plugin in self.active_plugins:
try:

View file

@ -5,6 +5,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from babase._stringedit import StringEditAdapter
import _babase
@ -24,9 +26,11 @@ class DevConsoleStringEditAdapter(StringEditAdapter):
description, initial_text, max_length, screen_space_center
)
@override
def _do_apply(self, new_text: str) -> None:
_babase.set_dev_console_input_text(new_text)
_babase.dev_console_input_adapter_finish()
@override
def _do_cancel(self) -> None:
_babase.dev_console_input_adapter_finish()

View file

@ -229,9 +229,7 @@ class AdsSubsystem:
await asyncio.sleep(1.0)
payload.run(fallback=True)
_fallback_task = babase.app.aioloop.create_task(
add_fallback_task()
)
babase.app.create_async_task(add_fallback_task())
self.show_ad('between_game', on_completion_call=payload.run)
else:
babase.pushcall(call) # Just run the callback without the ad.

View file

@ -4,8 +4,10 @@
from __future__ import annotations
import random
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import bascenev1
import _baclassic
@ -42,70 +44,81 @@ def run_cpu_benchmark() -> None:
cfg['Graphics Quality'] = self._old_quality
cfg.apply()
@override
def on_player_request(self, player: bascenev1.SessionPlayer) -> bool:
return False
bascenev1.new_host_session(BenchmarkSession, benchmark_type='cpu')
@dataclass
class _StressTestArgs:
playlist_type: str
playlist_name: str
player_count: int
round_duration: int
attract_mode: bool
def run_stress_test(
playlist_type: str = 'Random',
playlist_name: str = '__default__',
player_count: int = 8,
round_duration: int = 30,
attract_mode: bool = False,
) -> None:
"""Run a stress test."""
babase.screenmessage(
"Beginning stress test.. use 'End Test' to stop testing.",
color=(1, 1, 0),
)
with babase.ContextRef.empty():
start_stress_test(
{
'playlist_type': playlist_type,
'playlist_name': playlist_name,
'player_count': player_count,
'round_duration': round_duration,
}
if not attract_mode:
babase.screenmessage(
"Beginning stress test.. use 'End Test' to stop testing.",
color=(1, 1, 0),
)
_start_stress_test(
_StressTestArgs(
playlist_type=playlist_type,
playlist_name=playlist_name,
player_count=player_count,
round_duration=round_duration,
attract_mode=attract_mode,
)
)
def stop_stress_test() -> None:
"""End a running stress test."""
_baclassic.set_stress_testing(False, 0)
assert babase.app.classic is not None
try:
if babase.app.classic.stress_test_reset_timer is not None:
babase.screenmessage('Ending stress test...', color=(1, 1, 0))
except Exception:
pass
babase.app.classic.stress_test_reset_timer = None
_baclassic.set_stress_testing(False, 0, False)
babase.app.classic.stress_test_update_timer = None
babase.app.classic.stress_test_update_timer_2 = None
def start_stress_test(args: dict[str, Any]) -> None:
def _start_stress_test(args: _StressTestArgs) -> None:
"""(internal)"""
from bascenev1 import DualTeamSession, FreeForAllSession
assert babase.app.classic is not None
appconfig = babase.app.config
playlist_type = args['playlist_type']
playlist_type = args.playlist_type
if playlist_type == 'Random':
if random.random() < 0.5:
playlist_type = 'Teams'
else:
playlist_type = 'Free-For-All'
babase.screenmessage(
'Running Stress Test (listType="'
+ playlist_type
+ '", listName="'
+ args['playlist_name']
+ '")...'
)
if not args.attract_mode:
babase.screenmessage(
'Running Stress Test (listType="'
+ playlist_type
+ '", listName="'
+ args.playlist_name
+ '")...'
)
if playlist_type == 'Teams':
appconfig['Team Tournament Playlist Selection'] = args['playlist_name']
appconfig['Team Tournament Playlist Selection'] = args.playlist_name
appconfig['Team Tournament Playlist Randomize'] = 1
babase.apptimer(
1.0,
@ -115,7 +128,7 @@ def start_stress_test(args: dict[str, Any]) -> None:
),
)
else:
appconfig['Free-for-All Playlist Selection'] = args['playlist_name']
appconfig['Free-for-All Playlist Selection'] = args.playlist_name
appconfig['Free-for-All Playlist Randomize'] = 1
babase.apptimer(
1.0,
@ -124,19 +137,38 @@ def start_stress_test(args: dict[str, Any]) -> None:
babase.Call(bascenev1.new_host_session, FreeForAllSession),
),
)
_baclassic.set_stress_testing(True, args['player_count'])
babase.app.classic.stress_test_reset_timer = babase.AppTimer(
args['round_duration'], babase.Call(_reset_stress_test, args)
_baclassic.set_stress_testing(True, args.player_count, args.attract_mode)
babase.app.classic.stress_test_update_timer = babase.AppTimer(
args.round_duration, babase.Call(_reset_stress_test, args)
)
if args.attract_mode:
babase.app.classic.stress_test_update_timer_2 = babase.AppTimer(
0.48, babase.Call(_update_attract_mode_test, args), repeat=True
)
def _reset_stress_test(args: dict[str, Any]) -> None:
_baclassic.set_stress_testing(False, args['player_count'])
babase.screenmessage('Resetting stress test...')
def _update_attract_mode_test(args: _StressTestArgs) -> None:
if babase.get_input_idle_time() < 5.0:
_reset_stress_test(args)
def _reset_stress_test(args: _StressTestArgs) -> None:
_baclassic.set_stress_testing(False, args.player_count, False)
if not args.attract_mode:
babase.screenmessage('Resetting stress test...')
session = bascenev1.get_foreground_host_session()
assert session is not None
session.end()
babase.apptimer(1.0, babase.Call(start_stress_test, args))
assert babase.app.classic is not None
babase.app.classic.stress_test_update_timer = None
babase.app.classic.stress_test_update_timer_2 = None
# For regular stress tests we keep the party going. For attract-mode
# we just end back at the main menu. If things are idle there then
# we'll get sent back to a new stress test.
if not args.attract_mode:
babase.apptimer(1.0, babase.Call(_start_stress_test, args))
def run_gpu_benchmark() -> None:

View file

@ -9,6 +9,7 @@ import threading
from enum import Enum
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import bascenev1
@ -68,6 +69,7 @@ class MasterServerV1CallThread(threading.Thread):
with self._context:
self._callback(arg)
@override
def run(self) -> None:
# pylint: disable=consider-using-with
# pylint: disable=too-many-branches

View file

@ -8,6 +8,7 @@ import random
import logging
import weakref
from typing_extensions import override
from efro.dataclassio import dataclass_from_dict
import babase
import bauiv1
@ -69,7 +70,8 @@ class ClassicSubsystem(babase.AppSubsystem):
# Misc.
self.tips: list[str] = []
self.stress_test_reset_timer: babase.AppTimer | None = None
self.stress_test_update_timer: babase.AppTimer | None = None
self.stress_test_update_timer_2: babase.AppTimer | None = None
self.value_test_defaults: dict = {}
self.special_offer: dict | None = None
self.ping_thread_count = 0
@ -148,6 +150,7 @@ class ClassicSubsystem(babase.AppSubsystem):
assert isinstance(self._env['legacy_user_agent_string'], str)
return self._env['legacy_user_agent_string']
@override
def on_app_loading(self) -> None:
from bascenev1lib.actor import spazappearance
from bascenev1lib import maps as stdmaps
@ -229,13 +232,16 @@ class ClassicSubsystem(babase.AppSubsystem):
self.accounts.on_app_loading()
@override
def on_app_suspend(self) -> None:
self.accounts.on_app_suspend()
@override
def on_app_unsuspend(self) -> None:
self.accounts.on_app_unsuspend()
self.music.on_app_unsuspend()
@override
def on_app_shutdown(self) -> None:
self.music.on_app_shutdown()
@ -555,11 +561,18 @@ class ClassicSubsystem(babase.AppSubsystem):
playlist_name: str = '__default__',
player_count: int = 8,
round_duration: int = 30,
attract_mode: bool = False,
) -> None:
"""Run a stress test."""
from baclassic._benchmark import run_stress_test as run
run(playlist_type, playlist_name, player_count, round_duration)
run(
playlist_type=playlist_type,
playlist_name=playlist_name,
player_count=player_count,
round_duration=round_duration,
attract_mode=attract_mode,
)
def get_input_device_mapped_value(
self, device: bascenev1.InputDevice, name: str

View file

@ -8,6 +8,7 @@ import threading
from collections import deque
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
from baclassic._music import MusicPlayer
@ -27,6 +28,7 @@ class MacMusicAppMusicPlayer(MusicPlayer):
self._thread = _MacMusicAppThread()
self._thread.start()
@override
def on_select_entry(
self,
callback: Callable[[Any], None],
@ -40,6 +42,7 @@ class MacMusicAppMusicPlayer(MusicPlayer):
callback, current_entry, selection_target_name
)
@override
def on_set_volume(self, volume: float) -> None:
self._thread.set_volume(volume)
@ -47,6 +50,7 @@ class MacMusicAppMusicPlayer(MusicPlayer):
"""Asynchronously fetch the list of available iTunes playlists."""
self._thread.get_playlists(callback)
@override
def on_play(self, entry: Any) -> None:
assert babase.app.classic is not None
music = babase.app.classic.music
@ -59,9 +63,11 @@ class MacMusicAppMusicPlayer(MusicPlayer):
entry_type,
)
@override
def on_stop(self) -> None:
self._thread.play_playlist(None)
@override
def on_app_shutdown(self) -> None:
self._thread.shutdown()
@ -77,6 +83,7 @@ class _MacMusicAppThread(threading.Thread):
self._current_playlist: str | None = None
self._orig_volume: int | None = None
@override
def run(self) -> None:
"""Run the Music.app thread."""
babase.set_thread_name('BA_MacMusicAppThread')

View file

@ -9,6 +9,7 @@ import logging
import threading
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
from baclassic._music import MusicPlayer
@ -33,6 +34,7 @@ class OSMusicPlayer(MusicPlayer):
# FIXME: should ask the C++ layer for these; just hard-coding for now.
return ['mp3', 'ogg', 'm4a', 'wav', 'flac', 'mid']
@override
def on_select_entry(
self,
callback: Callable[[Any], None],
@ -48,9 +50,11 @@ class OSMusicPlayer(MusicPlayer):
callback, current_entry, selection_target_name
)
@override
def on_set_volume(self, volume: float) -> None:
babase.music_player_set_volume(volume)
@override
def on_play(self, entry: Any) -> None:
assert babase.app.classic is not None
music = babase.app.classic.music
@ -99,11 +103,13 @@ class OSMusicPlayer(MusicPlayer):
self._actually_playing = True
babase.music_player_play(result)
@override
def on_stop(self) -> None:
self._want_to_play = False
self._actually_playing = False
babase.music_player_stop()
@override
def on_app_shutdown(self) -> None:
babase.music_player_shutdown()
@ -120,6 +126,7 @@ class _PickFolderSongThread(threading.Thread):
self._callback = callback
self._path = path
@override
def run(self) -> None:
do_log_error = True
try:

View file

@ -21,7 +21,7 @@ class BuildInfoSet:
@dataclass
class Entry:
"""Info about a particular build."""
"""Info about a particular app build."""
filename: Annotated[str, IOAttrs('fname')]
size: Annotated[int, IOAttrs('size')]

View file

@ -7,6 +7,7 @@ from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Annotated
from enum import Enum
from typing_extensions import override
from efro.message import Message, Response
from efro.dataclassio import ioprepped, IOAttrs
from bacommon.transfer import DirectoryManifest
@ -21,6 +22,7 @@ if TYPE_CHECKING:
class LoginProxyRequestMessage(Message):
"""Request send to the cloud to ask for a login-proxy."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [LoginProxyRequestResponse]
@ -49,6 +51,7 @@ class LoginProxyStateQueryMessage(Message):
proxyid: Annotated[str, IOAttrs('p')]
proxykey: Annotated[str, IOAttrs('k')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [LoginProxyStateQueryResponse]
@ -85,6 +88,7 @@ class LoginProxyCompleteMessage(Message):
class PingMessage(Message):
"""Standard ping."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [PingResponse]
@ -103,6 +107,7 @@ class TestMessage(Message):
testfoo: Annotated[int, IOAttrs('f')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [TestResponse]
@ -116,6 +121,28 @@ class TestResponse(Response):
testfoo: Annotated[int, IOAttrs('f')]
@ioprepped
@dataclass
class PromoCodeMessage(Message):
"""User is entering a promo code"""
code: Annotated[str, IOAttrs('c')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [PromoCodeResponse]
@ioprepped
@dataclass
class PromoCodeResponse(Response):
"""Applied that promo code for ya, boss."""
valid: Annotated[bool, IOAttrs('v')]
message: Annotated[str | None, IOAttrs('m', store_default=False)] = None
@ioprepped
@dataclass
class WorkspaceFetchState:
@ -136,6 +163,7 @@ class WorkspaceFetchMessage(Message):
workspaceid: Annotated[str, IOAttrs('w')]
state: Annotated[WorkspaceFetchState, IOAttrs('s')]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [WorkspaceFetchResponse]
@ -162,6 +190,7 @@ class WorkspaceFetchResponse(Response):
class MerchAvailabilityMessage(Message):
"""Can we show merch link?"""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [MerchAvailabilityResponse]
@ -187,6 +216,7 @@ class SignInMessage(Message):
description: Annotated[str, IOAttrs('d', soft_default='-')]
apptime: Annotated[float, IOAttrs('at', soft_default=-1.0)]
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [SignInResponse]
@ -205,6 +235,7 @@ class SignInResponse(Response):
class ManageAccountMessage(Message):
"""Message asking for a manage-account url."""
@override
@classmethod
def get_response_types(cls) -> list[type[Response] | None]:
return [ManageAccountResponse]

View file

@ -31,7 +31,7 @@ class DirectoryManifest:
files: Annotated[dict[str, DirectoryManifestFile], IOAttrs('f')]
_empty_hash: str | None = None
# _empty_hash: str | None = None
@classmethod
def create_from_disk(cls, path: Path) -> DirectoryManifest:
@ -92,12 +92,12 @@ class DirectoryManifest:
)
break # 1 error is enough for now.
@classmethod
def get_empty_hash(cls) -> str:
"""Return the hash for an empty file."""
if cls._empty_hash is None:
import hashlib
# @classmethod
# def get_empty_hash(cls) -> str:
# """Return the hash for an empty file."""
# if cls._empty_hash is None:
# import hashlib
sha = hashlib.sha256()
cls._empty_hash = sha.hexdigest()
return cls._empty_hash
# sha = hashlib.sha256()
# cls._empty_hash = sha.hexdigest()
# return cls._empty_hash

View file

@ -52,8 +52,8 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
TARGET_BALLISTICA_BUILD = 21739
TARGET_BALLISTICA_VERSION = '1.7.32'
TARGET_BALLISTICA_BUILD = 21762
TARGET_BALLISTICA_VERSION = '1.7.33'
@dataclass
@ -350,9 +350,15 @@ def _setup_paths(
# platforms where there is no write access to said built-in
# stuff.
check_dir = Path(user_python_dir, 'sys', TARGET_BALLISTICA_VERSION)
if check_dir.is_dir():
app_python_dir = str(check_dir)
is_user_app_python_dir = True
try:
if check_dir.is_dir():
app_python_dir = str(check_dir)
is_user_app_python_dir = True
except PermissionError:
logging.warning(
"PermissionError checking user-app-python-dir path '%s'.",
check_dir,
)
# Ok, now apply these to sys.path.

View file

@ -16,9 +16,11 @@ from __future__ import annotations
import logging
from baplus._cloud import CloudSubsystem
from baplus._subsystem import PlusSubsystem
__all__ = [
'CloudSubsystem',
'PlusSubsystem',
]

214
dist/ba_data/python/baplus/_cloud.py vendored Normal file
View file

@ -0,0 +1,214 @@
# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to the cloud."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, overload
import babase
if TYPE_CHECKING:
from typing import Callable, Any
from efro.message import Message, Response
import bacommon.cloud
DEBUG_LOG = False
# TODO: Should make it possible to define a protocol in bacommon.cloud and
# autogenerate this. That would give us type safety between this and
# internal protocols.
class CloudSubsystem(babase.AppSubsystem):
"""Manages communication with cloud components."""
@property
def connected(self) -> bool:
"""Property equivalent of CloudSubsystem.is_connected()."""
return self.is_connected()
def is_connected(self) -> bool:
"""Return whether a connection to the cloud is present.
This is a good indicator (though not for certain) that sending
messages will succeed.
"""
return False # Needs to be overridden
def on_connectivity_changed(self, connected: bool) -> None:
"""Called when cloud connectivity state changes."""
if DEBUG_LOG:
logging.debug('CloudSubsystem: Connectivity is now %s.', connected)
plus = babase.app.plus
assert plus is not None
# Inform things that use this.
# (TODO: should generalize this into some sort of registration system)
plus.accounts.on_cloud_connectivity_changed(connected)
@overload
def send_message_cb(
self,
msg: bacommon.cloud.LoginProxyRequestMessage,
on_response: Callable[
[bacommon.cloud.LoginProxyRequestResponse | Exception], None
],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.LoginProxyStateQueryMessage,
on_response: Callable[
[bacommon.cloud.LoginProxyStateQueryResponse | Exception], None
],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.LoginProxyCompleteMessage,
on_response: Callable[[None | Exception], None],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.PingMessage,
on_response: Callable[[bacommon.cloud.PingResponse | Exception], None],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.SignInMessage,
on_response: Callable[
[bacommon.cloud.SignInResponse | Exception], None
],
) -> None:
...
@overload
def send_message_cb(
self,
msg: bacommon.cloud.ManageAccountMessage,
on_response: Callable[
[bacommon.cloud.ManageAccountResponse | Exception], None
],
) -> None:
...
def send_message_cb(
self,
msg: Message,
on_response: Callable[[Any], None],
) -> None:
"""Asynchronously send a message to the cloud from the logic thread.
The provided on_response call will be run in the logic thread
and passed either the response or the error that occurred.
"""
del msg # Unused.
babase.pushcall(
babase.Call(
on_response,
RuntimeError('Cloud functionality is not available.'),
)
)
@overload
def send_message(
self, msg: bacommon.cloud.WorkspaceFetchMessage
) -> bacommon.cloud.WorkspaceFetchResponse:
...
@overload
def send_message(
self, msg: bacommon.cloud.MerchAvailabilityMessage
) -> bacommon.cloud.MerchAvailabilityResponse:
...
@overload
def send_message(
self, msg: bacommon.cloud.TestMessage
) -> bacommon.cloud.TestResponse:
...
def send_message(self, msg: Message) -> Response | None:
"""Synchronously send a message to the cloud.
Must be called from a background thread.
"""
raise RuntimeError('Cloud functionality is not available.')
@overload
async def send_message_async(
self, msg: bacommon.cloud.PromoCodeMessage
) -> bacommon.cloud.PromoCodeResponse:
...
@overload
async def send_message_async(
self, msg: bacommon.cloud.TestMessage
) -> bacommon.cloud.TestResponse:
...
async def send_message_async(self, msg: Message) -> Response | None:
"""Synchronously send a message to the cloud.
Must be called from the logic thread.
"""
raise RuntimeError('Cloud functionality is not available.')
def cloud_console_exec(code: str) -> None:
"""Called by the cloud console to run code in the logic thread."""
import sys
import __main__
try:
# First try it as eval.
try:
evalcode = compile(code, '<console>', 'eval')
except SyntaxError:
evalcode = None
except Exception:
# hmm; when we can't compile it as eval will we always get
# syntax error?
logging.exception(
'unexpected error compiling code for cloud-console eval.'
)
evalcode = None
if evalcode is not None:
# pylint: disable=eval-used
value = eval(evalcode, vars(__main__), vars(__main__))
# For eval-able statements, print the resulting value if
# it is not None (just like standard Python interpreter).
if value is not None:
print(repr(value), file=sys.stderr)
# Fall back to exec if we couldn't compile it as eval.
else:
execcode = compile(code, '<console>', 'exec')
# pylint: disable=exec-used
exec(execcode, vars(__main__), vars(__main__))
except Exception:
import traceback
apptime = babase.apptime()
print(f'Exec error at time {apptime:.2f}.', file=sys.stderr)
traceback.print_exc()
# This helps the logging system ship stderr back to the
# cloud promptly.
sys.stderr.flush()

View file

@ -5,13 +5,17 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import _baplus
from typing_extensions import override
from babase import AppSubsystem
import _baplus
if TYPE_CHECKING:
from typing import Callable, Any
from babase import CloudSubsystem, AccountV2Subsystem
from babase import AccountV2Subsystem
from baplus._cloud import CloudSubsystem
class PlusSubsystem(AppSubsystem):
@ -32,6 +36,7 @@ class PlusSubsystem(AppSubsystem):
accounts: AccountV2Subsystem
cloud: CloudSubsystem
@override
def on_app_loading(self) -> None:
_baplus.on_app_loading()
self.accounts.on_app_loading()

View file

@ -103,6 +103,7 @@ from _bascenev1 import (
host_scan_cycle,
InputDevice,
is_in_replay,
is_replay_paused,
ls_input_devices,
ls_objects,
Material,
@ -112,11 +113,13 @@ from _bascenev1 import (
newactivity,
newnode,
Node,
pause_replay,
printnodes,
protocol_version,
release_gamepad_input,
release_keyboard_input,
reset_random_player_names,
resume_replay,
broadcastmessage,
SessionData,
SessionPlayer,
@ -352,6 +355,7 @@ __all__ = [
'IntSetting',
'is_in_replay',
'is_point_in_box',
'is_replay_paused',
'JoinActivity',
'Level',
'Lobby',
@ -374,6 +378,7 @@ __all__ = [
'normalized_color',
'NotFoundError',
'OutOfBoundsMessage',
'pause_replay',
'PickedUpMessage',
'PickUpMessage',
'Player',
@ -394,6 +399,7 @@ __all__ = [
'release_gamepad_input',
'release_keyboard_input',
'reset_random_player_names',
'resume_replay',
'safecolor',
'screenmessage',
'SceneV1AppMode',

View file

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -34,11 +35,13 @@ class EndSessionActivity(Activity[EmptyPlayer, EmptyTeam]):
self.inherits_vr_camera_offset = True
self.inherits_vr_overlay_center = True
@override
def on_transition_in(self) -> None:
super().on_transition_in()
babase.fade_screen(False)
babase.lock_all_input()
@override
def on_begin(self) -> None:
# pylint: disable=cyclic-import
@ -77,6 +80,7 @@ class JoinActivity(Activity[EmptyPlayer, EmptyTeam]):
self._tips_text: bascenev1.Actor | None = None
self._join_info: JoinInfo | None = None
@override
def on_transition_in(self) -> None:
# pylint: disable=cyclic-import
from bascenev1lib.actor.tipstext import TipsText
@ -110,6 +114,7 @@ class TransitionActivity(Activity[EmptyPlayer, EmptyTeam]):
super().__init__(settings)
self._background: bascenev1.Actor | None = None
@override
def on_transition_in(self) -> None:
# pylint: disable=cyclic-import
from bascenev1lib.actor.background import Background
@ -119,6 +124,7 @@ class TransitionActivity(Activity[EmptyPlayer, EmptyTeam]):
fade_time=0.5, start_faded=False, show_logo=False
)
@override
def on_begin(self) -> None:
super().on_begin()
@ -152,6 +158,7 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
self._custom_continue_message: babase.Lstr | None = None
self._server_transitioning: bool | None = None
@override
def on_player_join(self, player: EmptyPlayer) -> None:
super().on_player_join(player)
time_till_assign = max(
@ -164,6 +171,7 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
time_till_assign, babase.WeakCall(self._safe_assign, player)
)
@override
def on_transition_in(self) -> None:
from bascenev1lib.actor.tipstext import TipsText
from bascenev1lib.actor.background import Background
@ -176,6 +184,7 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
self._tips_text = TipsText()
setmusic(self.default_music)
@override
def on_begin(self) -> None:
# pylint: disable=cyclic-import
from bascenev1lib.actor.text import Text

View file

@ -5,8 +5,15 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from bacommon.app import AppExperience
from babase import AppMode, AppIntentExec, AppIntentDefault
from babase import (
app,
AppMode,
AppIntentExec,
AppIntentDefault,
invoke_main_menu,
)
import _bascenev1
@ -17,15 +24,18 @@ if TYPE_CHECKING:
class SceneV1AppMode(AppMode):
"""Our app-mode."""
@override
@classmethod
def get_app_experience(cls) -> AppExperience:
return AppExperience.MELEE
@override
@classmethod
def _supports_intent(cls, intent: AppIntent) -> bool:
# We support default and exec intents currently.
return isinstance(intent, AppIntentExec | AppIntentDefault)
@override
def handle_intent(self, intent: AppIntent) -> None:
if isinstance(intent, AppIntentExec):
_bascenev1.handle_app_intent_exec(intent.code)
@ -33,10 +43,19 @@ class SceneV1AppMode(AppMode):
assert isinstance(intent, AppIntentDefault)
_bascenev1.handle_app_intent_default()
@override
def on_activate(self) -> None:
# Let the native layer do its thing.
_bascenev1.on_app_mode_activate()
@override
def on_deactivate(self) -> None:
# Let the native layer do its thing.
_bascenev1.on_app_mode_deactivate()
@override
def on_app_active_changed(self) -> None:
# If we've gone inactive, bring up the main menu, which has the
# side effect of pausing the action (when possible).
if not app.active:
invoke_main_menu()

View file

@ -6,6 +6,7 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import override
import babase
import _bascenev1
@ -31,6 +32,7 @@ class CoopGameActivity(GameActivity[PlayerT, TeamT]):
# We can assume our session is a CoopSession.
session: bascenev1.CoopSession
@override
@classmethod
def supports_session_type(
cls, sessiontype: type[bascenev1.Session]
@ -49,6 +51,7 @@ class CoopGameActivity(GameActivity[PlayerT, TeamT]):
self._life_warning_beep_timer: bascenev1.Timer | None = None
self._warn_beeps_sound = _bascenev1.getsound('warnBeeps')
@override
def on_begin(self) -> None:
super().on_begin()
@ -139,6 +142,7 @@ class CoopGameActivity(GameActivity[PlayerT, TeamT]):
)
vval -= 55
@override
def spawn_player_spaz(
self,
player: PlayerT,

View file

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -97,6 +98,7 @@ class CoopSession(Session):
"""Get the game instance currently being played."""
return self._current_game_instance
@override
def should_allow_mid_activity_joins(
self, activity: bascenev1.Activity
) -> bool:
@ -174,9 +176,11 @@ class CoopSession(Session):
self._tutorial_activity = _bascenev1.newactivity(TutorialActivity)
@override
def get_custom_menu_entries(self) -> list[dict[str, Any]]:
return self._custom_menu_ui
@override
def on_player_leave(self, sessionplayer: bascenev1.SessionPlayer) -> None:
super().on_player_leave(sessionplayer)
@ -256,6 +260,7 @@ class CoopSession(Session):
activity.end(results={'outcome': 'restart'}, force=True)
# noinspection PyUnresolvedReferences
@override
def on_activity_end(
self, activity: bascenev1.Activity, results: Any
) -> None:

View file

@ -7,6 +7,7 @@ from __future__ import annotations
import weakref
from typing import Generic, TypeVar, TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -313,6 +314,7 @@ class AssetPackage(DependencyComponent):
self.package_id = entry.config
print(f'LOADING ASSET PACKAGE {self.package_id}')
@override
@classmethod
def dep_is_present(cls, config: Any = None) -> bool:
assert isinstance(config, str)

View file

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -32,6 +33,7 @@ class DualTeamSession(MultiTeamSession):
babase.increment_analytics_count('Teams session start')
super().__init__()
@override
def _switch_to_score_screen(self, results: bascenev1.GameResults) -> None:
# pylint: disable=cyclic-import
from bascenev1lib.activity.multiteamvictory import (

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -53,6 +54,7 @@ class FreeForAllSession(MultiTeamSession):
babase.increment_analytics_count('Free-for-all session start')
super().__init__()
@override
def _switch_to_score_screen(self, results: bascenev1.GameResults) -> None:
# pylint: disable=cyclic-import
from efro.util import asserttype

View file

@ -9,6 +9,7 @@ import random
import logging
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import override
import babase
import _bascenev1
@ -377,6 +378,7 @@ class GameActivity(Activity[PlayerT, TeamT]):
"""
return ''
@override
def on_transition_in(self) -> None:
super().on_transition_in()
@ -488,6 +490,7 @@ class GameActivity(Activity[PlayerT, TeamT]):
self.end_game()
@override
def on_begin(self) -> None:
super().on_begin()
@ -536,12 +539,14 @@ class GameActivity(Activity[PlayerT, TeamT]):
max(5, data_t[0]['timeRemaining'])
)
@override
def on_player_join(self, player: PlayerT) -> None:
super().on_player_join(player)
# By default, just spawn a dude.
self.spawn_player(player)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, PlayerDiedMessage):
# pylint: disable=cyclic-import
@ -835,6 +840,7 @@ class GameActivity(Activity[PlayerT, TeamT]):
animate(combine, 'input3', {0: 0, 1.0: 1, 4.0: 1, 5.0: 0})
_bascenev1.timer(5.0, tnode.delete)
@override
def end(
self, results: Any = None, delay: float = 0.0, force: bool = False
) -> None:

View file

@ -7,6 +7,7 @@ import copy
import weakref
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
if TYPE_CHECKING:
@ -38,6 +39,7 @@ class Level:
self._index: int | None = None
self._score_version_string: str | None = None
@override
def __repr__(self) -> str:
cls = type(self)
return f"<{cls.__module__}.{cls.__name__} '{self._name}'>"

View file

@ -441,7 +441,7 @@ class Chooser:
# list might have changed.
input_device = self._sessionplayer.inputdevice
is_remote = input_device.is_remote_client
is_test_input = input_device.name.startswith('TestInput')
is_test_input = input_device.is_test_input
# Pull this player's list of unlocked characters.
if is_remote:

View file

@ -6,6 +6,7 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
@ -353,9 +354,11 @@ class Map(Actor):
return self.flag_points_default[:3]
return self.flag_points[team_index % len(self.flag_points)][:3]
@override
def exists(self) -> bool:
return bool(self.node)
@override
def handlemessage(self, msg: Any) -> Any:
from bascenev1 import _messages

View file

@ -8,7 +8,9 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bascenev1
from bascenev1._session import Session
@ -70,6 +72,10 @@ class MultiTeamSession(Session):
show_tutorial = cfg.get('Show Tutorial', True)
# Special case: don't show tutorial while stress testing.
if classic.stress_test_update_timer is not None:
show_tutorial = False
self._tutorial_activity_instance: bascenev1.Activity | None
if show_tutorial:
from bascenev1lib.tutorial import TutorialActivity
@ -164,6 +170,7 @@ class MultiTeamSession(Session):
"""Returns which game in the series is currently being played."""
return self._game_number
@override
def on_team_join(self, team: bascenev1.SessionTeam) -> None:
team.customdata['previous_score'] = team.customdata['score'] = 0
@ -182,6 +189,7 @@ class MultiTeamSession(Session):
self._next_game_spec['settings'],
)
@override
def on_activity_end(
self, activity: bascenev1.Activity, results: Any
) -> None:

View file

@ -6,6 +6,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from bascenev1._messages import DieMessage
from bascenev1._actor import Actor
@ -28,6 +30,7 @@ class NodeActor(Actor):
super().__init__()
self.node = node
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, DieMessage):
if self.node:
@ -35,5 +38,6 @@ class NodeActor(Actor):
return None
return super().handlemessage(msg)
@override
def exists(self) -> bool:
return bool(self.node)

View file

@ -253,7 +253,7 @@ class Session:
# Limit player counts *unless* we're in a stress test.
if (
babase.app.classic is not None
and babase.app.classic.stress_test_reset_timer is None
and babase.app.classic.stress_test_update_timer is None
):
if len(self.sessionplayers) >= self.max_players:
# Print a rejection message *only* to the client trying to

View file

@ -7,6 +7,7 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import override
import babase
import _bascenev1
@ -35,6 +36,7 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
bascenev1.Player has their own bascenev1.Team)
"""
@override
@classmethod
def supports_session_type(
cls, sessiontype: type[bascenev1.Session]
@ -57,6 +59,7 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
if isinstance(self.session, FreeForAllSession):
self.show_kill_points = False
@override
def on_transition_in(self) -> None:
# pylint: disable=cyclic-import
from bascenev1._coopsession import CoopSession
@ -67,7 +70,9 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
# On the first game, show the controls UI momentarily.
# (unless we're being run in co-op mode, in which case we leave
# it up to them)
if not isinstance(self.session, CoopSession):
if not isinstance(self.session, CoopSession) and getattr(
self, 'show_controls_guide', True
):
attrname = '_have_shown_ctrl_help_overlay'
if not getattr(self.session, attrname, False):
delay = 4.0
@ -83,6 +88,7 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
).autoretain()
setattr(self.session, attrname, True)
@override
def on_begin(self) -> None:
super().on_begin()
try:
@ -102,6 +108,7 @@ class TeamGameActivity(GameActivity[PlayerT, TeamT]):
except Exception:
logging.exception('Error in on_begin.')
@override
def spawn_player_spaz(
self,
player: PlayerT,

View file

@ -4,6 +4,7 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
@ -18,6 +19,7 @@ class CoopJoinActivity(bs.JoinActivity):
session = self.session
assert isinstance(session, bs.CoopSession)
@override
def on_transition_in(self) -> None:
from bascenev1lib.actor.controlsguide import ControlsGuide
from bascenev1lib.actor.text import Text

View file

@ -9,6 +9,7 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
from bacommon.login import LoginType
import bascenev1 as bs
import bauiv1 as bui
@ -186,6 +187,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
self._victory: bool = settings['outcome'] == 'victory'
@override
def __del__(self) -> None:
super().__del__()
@ -194,6 +196,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
with bui.ContextRef.empty():
bui.containerwidget(edit=self._root_ui, transition='out_left')
@override
def on_transition_in(self) -> None:
from bascenev1lib.actor import background # FIXME NO BSSTD
@ -574,6 +577,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
self._player_press,
)
@override
def on_player_join(self, player: bs.Player) -> None:
super().on_player_join(player)
@ -585,6 +589,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
bs.timer(time_till_assign, bs.WeakCall(self._safe_assign, player))
@override
def on_begin(self) -> None:
# FIXME: Clean this up.
# pylint: disable=too-many-statements

View file

@ -4,7 +4,9 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from bascenev1lib.actor.zoomtext import ZoomText
@ -14,6 +16,7 @@ class DrawScoreScreenActivity(MultiTeamScoreScreenActivity):
default_music = None # Awkward silence...
@override
def on_begin(self) -> None:
bs.set_analytics_screen('Draw Score Screen')
super().on_begin()

View file

@ -4,7 +4,9 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from bascenev1lib.actor.zoomtext import ZoomText
@ -17,6 +19,7 @@ class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
self._winner: bs.SessionTeam = settings['winner']
assert isinstance(self._winner, bs.SessionTeam)
@override
def on_begin(self) -> None:
bs.set_analytics_screen('Teams Score Screen')
super().on_begin()

View file

@ -6,9 +6,11 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
if TYPE_CHECKING:
from typing import Any
@ -23,6 +25,7 @@ class FreeForAllVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
self.transition_time = 0.5
self._cymbal_sound = bs.getsound('cymbal')
@override
def on_begin(self) -> None:
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements

View file

@ -4,7 +4,9 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.text import Text
@ -15,6 +17,7 @@ class MultiTeamJoinActivity(bs.JoinActivity):
super().__init__(settings)
self._next_up_text: Text | None = None
@override
def on_transition_in(self) -> None:
from bascenev1lib.actor.controlsguide import ControlsGuide

View file

@ -3,7 +3,9 @@
"""Functionality related to teams mode score screen."""
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.text import Text
from bascenev1lib.actor.image import Image
@ -18,6 +20,7 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
self._show_up_next: bool = True
@override
def on_begin(self) -> None:
super().on_begin()
session = self.session

View file

@ -4,7 +4,9 @@
from __future__ import annotations
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
@ -22,6 +24,7 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
self._tips_text = None
self._default_show_tips = False
@override
def on_begin(self) -> None:
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals

View file

@ -9,6 +9,7 @@ import weakref
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -104,6 +105,7 @@ class Background(bs.Actor):
timeval += random.random() * 0.1
bs.animate(cmb, 'input1', keys, loop=True)
@override
def __del__(self) -> None:
# Normal actors don't get sent DieMessages when their
# activity is shutting down, but we still need to do so
@ -138,6 +140,7 @@ class Background(bs.Actor):
)
bs.timer(self.fade_time + 0.1, self.node.delete)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -10,7 +10,9 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING, TypeVar
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
@ -661,6 +663,7 @@ class Blast(bs.Actor):
bs.timer(0.4, _extra_debris_sound)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
@ -935,6 +938,7 @@ class Bomb(bs.Actor):
else None
)
@override
def on_expire(self) -> None:
super().on_expire()
@ -1140,19 +1144,18 @@ class Bomb(bs.Actor):
if msg.srcnode:
pass
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, ExplodeMessage):
self.explode()
elif isinstance(msg, ImpactMessage):
self._handle_impact()
# Ok the logic below looks like it was backwards to me.
# Disabling for now; can bring back if need be.
# elif isinstance(msg, bs.PickedUpMessage):
# # Change our source to whoever just picked us up *only* if it
# # is None. This way we can get points for killing bots with their
# # own bombs. Hmm would there be a downside to this?
# if self._source_player is not None:
# self._source_player = msg.node.source_player
elif isinstance(msg, bs.PickedUpMessage):
# Change our source to whoever just picked us up *only* if it
# is None. This way we can get points for killing bots with their
# own bombs. Hmm would there be a downside to this?
if self._source_player is None:
self._source_player = msg.node.source_player
elif isinstance(msg, SplatMessage):
self._handle_splat()
elif isinstance(msg, bs.DroppedMessage):

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -547,9 +548,11 @@ class ControlsGuide(bs.Actor):
self._update_timer = None
self._dead = True
@override
def exists(self) -> bool:
return not self._dead
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -7,9 +7,11 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
from bascenev1lib.gameutils import SharedObjects
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence
@ -328,6 +330,7 @@ class Flag(bs.Actor):
1.0, bs.WeakCall(self._hide_score_text)
)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -7,6 +7,7 @@ from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -165,6 +166,7 @@ class Image(bs.Actor):
bs.WeakCall(self.handlemessage, bs.DieMessage()),
)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -72,6 +73,7 @@ class OnScreenCountdown(bs.Actor):
)
self._timer = bs.Timer(1.0, self._update, repeat=True)
@override
def on_expire(self) -> None:
super().on_expire()

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import logging
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -93,6 +94,7 @@ class OnScreenTimer(bs.Actor):
"""Shortcut for start time in seconds."""
return self.getstarttime()
@override
def handlemessage(self, msg: Any) -> Any:
# if we're asked to die, just kill our node/timer
if isinstance(msg, bs.DieMessage):

View file

@ -6,7 +6,9 @@ from __future__ import annotations
from typing import TYPE_CHECKING, TypeVar, overload
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.spaz import Spaz
if TYPE_CHECKING:
@ -183,6 +185,7 @@ class PlayerSpaz(Spaz):
' non-connected player'
)
@override
def handlemessage(self, msg: Any) -> Any:
# FIXME: Tidy this up.
# pylint: disable=too-many-branches

View file

@ -7,6 +7,7 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -118,6 +119,7 @@ class PopupText(bs.Actor):
lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage())
)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -7,7 +7,9 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
@ -278,6 +280,7 @@ class PowerupBox(bs.Actor):
if self.node:
self.node.flashing = True
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired

View file

@ -9,11 +9,13 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.bomb import Bomb, Blast
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
from bascenev1lib.actor.spazfactory import SpazFactory
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence, Callable
@ -228,9 +230,11 @@ class Spaz(bs.Actor):
self.punch_callback: Callable[[Spaz], Any] | None = None
self.pick_up_powerup_callback: Callable[[Spaz], Any] | None = None
@override
def exists(self) -> bool:
return bool(self.node)
@override
def on_expire(self) -> None:
super().on_expire()
@ -249,6 +253,7 @@ class Spaz(bs.Actor):
assert not self.expired
self._dropped_bomb_callbacks.append(call)
@override
def is_alive(self) -> bool:
"""
Method override; returns whether ol' spaz is still kickin'.
@ -694,6 +699,7 @@ class Spaz(bs.Actor):
else:
self.shield_decay_timer = None
@override
def handlemessage(self, msg: Any) -> Any:
# pylint: disable=too-many-return-statements
# pylint: disable=too-many-statements

View file

@ -10,6 +10,7 @@ import weakref
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.spaz import Spaz
@ -489,6 +490,7 @@ class SpazBot(Spaz):
self.on_punch_press()
self.on_punch_release()
@override
def on_punched(self, damage: int) -> None:
"""
Method override; sends bs.SpazBotPunchedMessage
@ -496,6 +498,7 @@ class SpazBot(Spaz):
"""
bs.getactivity().handlemessage(SpazBotPunchedMessage(self, damage))
@override
def on_expire(self) -> None:
super().on_expire()
@ -503,6 +506,7 @@ class SpazBot(Spaz):
# no chance of them keeping activities or other things alive.
self.update_callback = None
@override
def handlemessage(self, msg: Any) -> Any:
# pylint: disable=too-many-branches
assert not self.expired

View file

@ -7,6 +7,7 @@ from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -221,6 +222,7 @@ class Text(bs.Actor):
bs.WeakCall(self.handlemessage, bs.DieMessage()),
)
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -6,6 +6,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -95,6 +96,7 @@ class TipsText(bs.Actor):
)
self.node.text = next_tip
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -8,6 +8,7 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
if TYPE_CHECKING:
@ -158,6 +159,7 @@ class ZoomText(bs.Actor):
if lifespan is not None:
bs.timer(lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage()))
@override
def handlemessage(self, msg: Any) -> Any:
assert not self.expired
if isinstance(msg, bs.DieMessage):

View file

@ -10,11 +10,13 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.flag import Flag
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -71,10 +73,12 @@ class AssaultGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -96,16 +100,19 @@ class AssaultGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FORWARD_MARCH
)
@override
def get_instance_description(self) -> str | Sequence:
if self._score_to_win == 1:
return 'Touch the enemy flag.'
return 'Touch the enemy flag ${ARG1} times.', self._score_to_win
@override
def get_instance_description_short(self) -> str | Sequence:
if self._score_to_win == 1:
return 'touch 1 flag'
return 'touch ${ARG1} flags', self._score_to_win
@override
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
shared = SharedObjects.get()
base_pos = self.map.get_flag_position(sessionteam.id)
@ -151,16 +158,19 @@ class AssaultGame(bs.TeamGameActivity[Player, Team]):
return team
@override
def on_team_join(self, team: Team) -> None:
# Can't do this in create_team because the team's color/etc. have
# not been wired up yet at that point.
self._update_scoreboard()
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
self.setup_standard_powerup_drops()
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment standard.
@ -249,6 +259,7 @@ class AssaultGame(bs.TeamGameActivity[Player, Team]):
if player_team.score >= self._score_to_win:
self.end_game()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:

View file

@ -10,6 +10,9 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.flag import (
@ -19,7 +22,6 @@ from bascenev1lib.actor.flag import (
FlagDroppedMessage,
FlagDiedMessage,
)
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -141,10 +143,12 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -173,16 +177,19 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FLAG_CATCHER
)
@override
def get_instance_description(self) -> str | Sequence:
if self._score_to_win == 1:
return 'Steal the enemy flag.'
return 'Steal the enemy flag ${ARG1} times.', self._score_to_win
@override
def get_instance_description_short(self) -> str | Sequence:
if self._score_to_win == 1:
return 'return 1 flag'
return 'return ${ARG1} flags', self._score_to_win
@override
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
# Create our team instance and its initial values.
@ -272,12 +279,14 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
return team
@override
def on_team_join(self, team: Team) -> None:
# Can't do this in create_team because the team's color/etc. have
# not been wired up yet at that point.
self._spawn_flag_for_team(team)
self._update_scoreboard()
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -406,6 +415,7 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
if team.score >= self._score_to_win:
self.end_game()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -532,6 +542,7 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
bs.animate(light, 'intensity', {0.0: 0, 0.25: 2.0, 0.5: 0}, loop=True)
bs.timer(length, light.delete)
@override
def spawn_player_spaz(
self,
player: Player,
@ -576,6 +587,7 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
team, team.score, self._score_to_win
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment standard behavior.

View file

@ -10,11 +10,13 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.flag import Flag
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -83,6 +85,7 @@ class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
]
scoreconfig = bs.ScoreConfig(label='Time Held')
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -121,20 +124,25 @@ class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.CHOSEN_ONE
)
@override
def get_instance_description(self) -> str | Sequence:
return 'There can be only one.'
@override
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
return Team(time_remaining=self._chosen_one_time)
@override
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
@override
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
if self._get_chosen_one_player() is player:
self._set_chosen_one_player(None)
@override
def on_begin(self) -> None:
super().on_begin()
shared = SharedObjects.get()
@ -251,6 +259,7 @@ class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
logging.error('got nonexistent player as chosen one in _tick')
self._set_chosen_one_player(None)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -335,6 +344,7 @@ class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
'position', light.node, 'position'
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.

View file

@ -10,12 +10,14 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.flag import Flag
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.respawnicon import RespawnIcon
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -108,10 +110,12 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -143,16 +147,20 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
),
)
@override
def get_instance_description(self) -> str | Sequence:
return 'Secure all ${ARG1} flags.', len(self.map.flag_points)
@override
def get_instance_description_short(self) -> str | Sequence:
return 'secure all ${ARG1} flags', len(self.map.flag_points)
@override
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scores()
@override
def on_player_join(self, player: Player) -> None:
player.respawn_timer = None
@ -160,6 +168,7 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
if player.team.flags_held > 0:
self.spawn_player(player)
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -221,6 +230,7 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
team, team.flags_held, len(self._flags)
)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -272,6 +282,7 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
):
self.spawn_player(otherplayer)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
@ -287,6 +298,7 @@ class ConquestGame(bs.TeamGameActivity[Player, Team]):
else:
super().handlemessage(msg)
@override
def spawn_player(self, player: Player) -> bs.Actor:
# We spawn players at different places based on what flags are held.
return self.spawn_player_spaz(

View file

@ -9,9 +9,11 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -38,6 +40,7 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
# Print messages when players die since it matters here.
announce_player_deaths = True
@override
@classmethod
def get_available_settings(
cls, sessiontype: type[bs.Session]
@ -87,12 +90,14 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
return settings
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession) or issubclass(
sessiontype, bs.FreeForAllSession
)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -116,16 +121,20 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.TO_THE_DEATH
)
@override
def get_instance_description(self) -> str | Sequence:
return 'Crush ${ARG1} of your enemies.', self._score_to_win
@override
def get_instance_description_short(self) -> str | Sequence:
return 'kill ${ARG1} enemies', self._score_to_win
@override
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scoreboard()
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -137,6 +146,7 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
)
self._update_scoreboard()
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
@ -197,6 +207,7 @@ class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
team, team.score, self._score_to_win
)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:

View file

@ -10,6 +10,9 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.spazbot import SpazBotSet, BouncyBot, SpazBotDiedMessage
@ -17,7 +20,6 @@ from bascenev1lib.actor.onscreencountdown import OnScreenCountdown
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.respawnicon import RespawnIcon
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any
@ -51,11 +53,13 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
scoreconfig = bs.ScoreConfig(label='Score', scoretype=bs.ScoreType.POINTS)
# We're currently hard-coded for one map.
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return ['Tower D']
# We support teams, free-for-all, and co-op sessions.
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return (
@ -93,11 +97,13 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FORWARD_MARCH
)
@override
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self._update_scoreboard()
# Called when our game actually starts.
@override
def on_begin(self) -> None:
from bascenev1lib.maps import TowerD
@ -118,6 +124,7 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
self._spawn_evil_bunny()
# Overriding the default character spawning.
@override
def spawn_player(self, player: Player) -> bs.Actor:
spaz = self.spawn_player_spaz(player)
spaz.connect_controls_to_player()
@ -191,6 +198,7 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
self._eggs.append(Egg(position=(xpos, ypos, zpos)))
# Various high-level game events come through this method.
@override
def handlemessage(self, msg: Any) -> Any:
# Respawn dead players.
if isinstance(msg, bs.PlayerDiedMessage):
@ -231,6 +239,7 @@ class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
for team in self.teams:
self._scoreboard.set_team_value(team, team.score)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -271,9 +280,11 @@ class Egg(bs.Actor):
},
)
@override
def exists(self) -> bool:
return bool(self.node)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
if self.node:

View file

@ -10,9 +10,11 @@ from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.spazfactory import SpazFactory
from bascenev1lib.actor.scoreboard import Scoreboard
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -157,6 +159,7 @@ class Icon(bs.Actor):
if lives == 0:
bs.timer(0.6, self.update_for_lives)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
self.node.delete()
@ -194,6 +197,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
allow_mid_activity_joins = False
@override
@classmethod
def get_available_settings(
cls, sessiontype: type[bs.Session]
@ -238,12 +242,14 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
)
return settings
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession) or issubclass(
sessiontype, bs.FreeForAllSession
)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -269,6 +275,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.SURVIVAL
)
@override
def get_instance_description(self) -> str | Sequence:
return (
'Last team standing wins.'
@ -276,6 +283,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
else 'Last one standing wins.'
)
@override
def get_instance_description_short(self) -> str | Sequence:
return (
'last team standing wins'
@ -283,6 +291,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
else 'last one standing wins'
)
@override
def on_player_join(self, player: Player) -> None:
player.lives = self._lives_per_player
@ -299,6 +308,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
if self.has_begun():
self._update_icons()
@override
def on_begin(self) -> None:
super().on_begin()
self._start_time = bs.time()
@ -473,6 +483,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
return points[-1][1]
return None
@override
def spawn_player(self, player: Player) -> bs.Actor:
actor = self.spawn_player_spaz(player, self._get_spawn_point(player))
if not self._solo_mode:
@ -499,6 +510,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
position=player.node.position,
).autoretain()
@override
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
player.icons = []
@ -522,6 +534,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
def _get_total_team_lives(self, team: Team) -> int:
return sum(player.lives for player in team.players)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
@ -592,6 +605,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
and any(player.lives > 0 for player in team.players)
]
@override
def end_game(self) -> None:
if self.has_ended():
return

View file

@ -1,5 +1,6 @@
# Released under the MIT License. See LICENSE for details.
#
# pylint: disable=too-many-lines
"""Implements football games (both co-op and teams varieties)."""
# ba_meta require api 8
@ -12,6 +13,9 @@ import random
import logging
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.bomb import TNTSpawner
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
@ -39,7 +43,6 @@ from bascenev1lib.actor.spazbot import (
StickyBot,
ExplodeyBot,
)
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -128,11 +131,13 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
# We only support two-team play.
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -170,6 +175,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.FOOTBALL
)
@override
def get_instance_description(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
@ -181,6 +187,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
return 'Score ${ARG1} touchdowns.', touchdowns
return 'Score a touchdown.'
@override
def get_instance_description_short(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
touchdowns = math.ceil(touchdowns)
@ -188,6 +195,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
return 'score ${ARG1} touchdowns', touchdowns
return 'score a touchdown'
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -224,6 +232,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
self._update_scoreboard()
self._chant_sound.play()
@override
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
@ -285,6 +294,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
bs.cameraflash(duration=10.0)
self._update_scoreboard()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -298,6 +308,7 @@ class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
team, team.score, self._score_to_win
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, FlagPickedUpMessage):
assert isinstance(msg.flag, FootballFlag)
@ -379,9 +390,11 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
default_music = bs.MusicType.FOOTBALL
# FIXME: Need to update co-op games to use getscoreconfig.
@override
def get_score_type(self) -> str:
return 'time'
@override
def get_instance_description(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
touchdowns = math.ceil(touchdowns)
@ -389,6 +402,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
return 'Score ${ARG1} touchdowns.', touchdowns
return 'Score a touchdown.'
@override
def get_instance_description_short(self) -> str | Sequence:
touchdowns = self._score_to_win / 7
touchdowns = math.ceil(touchdowns)
@ -444,6 +458,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
self._flag_respawn_light: bs.Actor | None = None
self._flag: FootballFlag | None = None
@override
def on_transition_in(self) -> None:
super().on_transition_in()
self._scoreboard = Scoreboard()
@ -480,6 +495,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
)
self._chant_sound.play()
@override
def on_begin(self) -> None:
# FIXME: Split this up a bit.
# pylint: disable=too-many-statements
@ -795,11 +811,13 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
if i == 0:
bs.cameraflash(duration=10.0)
@override
def end_game(self) -> None:
bs.setmusic(None)
self._bots.final_celebrate()
bs.timer(0.001, bs.Call(self.do_end, 'defeat'))
@override
def on_continue(self) -> None:
# Subtract one touchdown from the bots and get them moving again.
assert self._bot_team is not None
@ -897,6 +915,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
},
)
@override
def handlemessage(self, msg: Any) -> Any:
"""handle high-level game messages"""
if isinstance(msg, bs.PlayerDiedMessage):
@ -959,6 +978,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
del player # Unused.
self._player_has_punched = True
@override
def spawn_player(self, player: Player) -> bs.Actor:
spaz = self.spawn_player_spaz(
player, position=self.map.get_start_position(player.team.id)

View file

@ -9,11 +9,13 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -58,6 +60,7 @@ class Puck(bs.Actor):
)
bs.animate(self.node, 'mesh_scale', {0: 0, 0.2: 1.3, 0.26: 1})
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
if self.node:
@ -152,10 +155,12 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
bs.BoolSetting('Epic Mode', default=False),
]
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -231,16 +236,19 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.HOCKEY
)
@override
def get_instance_description(self) -> str | Sequence:
if self._score_to_win == 1:
return 'Score a goal.'
return 'Score ${ARG1} goals.', self._score_to_win
@override
def get_instance_description_short(self) -> str | Sequence:
if self._score_to_win == 1:
return 'score a goal'
return 'score ${ARG1} goals', self._score_to_win
@override
def on_begin(self) -> None:
super().on_begin()
@ -281,6 +289,7 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
self._update_scoreboard()
self._chant_sound.play()
@override
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
@ -364,6 +373,7 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
bs.cameraflash(duration=10.0)
self._update_scoreboard()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -375,6 +385,7 @@ class HockeyGame(bs.TeamGameActivity[Player, Team]):
for team in self.teams:
self._scoreboard.set_team_value(team, team.score, winscore)
@override
def handlemessage(self, msg: Any) -> Any:
# Respawn dead players if they're still in the game.
if isinstance(msg, bs.PlayerDiedMessage):

View file

@ -11,6 +11,9 @@ import logging
from enum import Enum
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.flag import (
@ -19,7 +22,6 @@ from bascenev1lib.actor.flag import (
FlagDiedMessage,
FlagPickedUpMessage,
)
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -86,12 +88,14 @@ class KeepAwayGame(bs.TeamGameActivity[Player, Team]):
]
scoreconfig = bs.ScoreConfig(label='Time Held')
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession) or issubclass(
sessiontype, bs.FreeForAllSession
)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -129,18 +133,23 @@ class KeepAwayGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.KEEP_AWAY
)
@override
def get_instance_description(self) -> str | Sequence:
return 'Carry the flag for ${ARG1} seconds.', self._hold_time
@override
def get_instance_description_short(self) -> str | Sequence:
return 'carry the flag for ${ARG1} seconds', self._hold_time
@override
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
return Team(timeremaining=self._hold_time)
@override
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
@override
def on_begin(self) -> None:
super().on_begin()
self.setup_standard_time_limit(self._time_limit)
@ -181,6 +190,7 @@ class KeepAwayGame(bs.TeamGameActivity[Player, Team]):
if scoreteam.timeremaining <= 0:
self.end_game()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -268,6 +278,7 @@ class KeepAwayGame(bs.TeamGameActivity[Player, Team]):
team, team.timeremaining, self._hold_time, countdown=True
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.

View file

@ -11,11 +11,13 @@ import weakref
from enum import Enum
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.flag import Flag
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -84,10 +86,12 @@ class KingOfTheHillGame(bs.TeamGameActivity[Player, Team]):
]
scoreconfig = bs.ScoreConfig(label='Time Held')
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.MultiTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -144,15 +148,19 @@ class KingOfTheHillGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.SCARY
)
@override
def get_instance_description(self) -> str | Sequence:
return 'Secure the flag for ${ARG1} seconds.', self._hold_time
@override
def get_instance_description_short(self) -> str | Sequence:
return 'secure the flag for ${ARG1} seconds', self._hold_time
@override
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
return Team(time_remaining=self._hold_time)
@override
def on_begin(self) -> None:
super().on_begin()
shared = SharedObjects.get()
@ -223,6 +231,7 @@ class KingOfTheHillGame(bs.TeamGameActivity[Player, Team]):
if scoring_team.time_remaining <= 0:
self.end_game()
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -283,6 +292,7 @@ class KingOfTheHillGame(bs.TeamGameActivity[Player, Team]):
team, team.time_remaining, self._hold_time, countdown=True
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
super().handlemessage(msg) # Augment default.

View file

@ -10,9 +10,11 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.actor.onscreentimer import OnScreenTimer
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -49,11 +51,13 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
allow_mid_activity_joins = False
# We're currently hard-coded for one map.
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return ['Rampage']
# We support teams, free-for-all, and co-op sessions.
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return (
@ -77,6 +81,7 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
if self._epic_mode:
self.slow_motion = True
@override
def on_begin(self) -> None:
super().on_begin()
@ -100,6 +105,7 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
# Check for immediate end (if we've only got 1 player, etc).
bs.timer(5.0, self._check_end_game)
@override
def on_player_leave(self, player: Player) -> None:
# Augment default behavior.
super().on_player_leave(player)
@ -108,6 +114,7 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
self._check_end_game()
# overriding the default character spawning..
@override
def spawn_player(self, player: Player) -> bs.Actor:
spaz = self.spawn_player_spaz(player)
@ -122,6 +129,7 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
return spaz
# Various high-level game events come through this method.
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior.
@ -213,6 +221,7 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
def _decrement_meteor_time(self) -> None:
self._meteor_time = max(0.01, self._meteor_time * 0.9)
@override
def end_game(self) -> None:
cur_time = bs.time()
assert self._timer is not None

View file

@ -10,13 +10,15 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.spazbot import (
SpazBotSet,
ChargerBot,
SpazBotDiedMessage,
)
from bascenev1lib.actor.onscreentimer import OnScreenTimer
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any
@ -44,6 +46,7 @@ class NinjaFightGame(bs.TeamGameActivity[Player, Team]):
)
default_music = bs.MusicType.TO_THE_DEATH
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
# For now we're hard-coding spawn positions and whatnot
@ -51,6 +54,7 @@ class NinjaFightGame(bs.TeamGameActivity[Player, Team]):
# a specific map.
return ['Courtyard']
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
# We currently support Co-Op only.
@ -67,6 +71,7 @@ class NinjaFightGame(bs.TeamGameActivity[Player, Team]):
self._preset = str(settings['preset'])
# Called when our game actually begins.
@override
def on_begin(self) -> None:
super().on_begin()
is_pro = self._preset == 'pro'
@ -123,6 +128,7 @@ class NinjaFightGame(bs.TeamGameActivity[Player, Team]):
)
# Called for each spawning player.
@override
def spawn_player(self, player: Player) -> bs.Actor:
# Let's spawn close to the center.
spawn_center = (0, 3, -2)
@ -144,6 +150,7 @@ class NinjaFightGame(bs.TeamGameActivity[Player, Team]):
self.end_game()
# Called for miscellaneous messages.
@override
def handlemessage(self, msg: Any) -> Any:
# A player has died.
if isinstance(msg, bs.PlayerDiedMessage):
@ -166,6 +173,7 @@ class NinjaFightGame(bs.TeamGameActivity[Player, Team]):
# When this is called, we should fill out results and end the game
# *regardless* of whether is has been won. (this may be called due
# to a tournament ending or other external reason).
@override
def end_game(self) -> None:
# Stop our on-screen timer so players can see what they got.
assert self._timer is not None

View file

@ -17,6 +17,9 @@ from enum import Enum, unique
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.popuptext import PopupText
from bascenev1lib.actor.bomb import TNTSpawner
from bascenev1lib.actor.playerspaz import PlayerSpazHurtMessage
@ -45,7 +48,6 @@ from bascenev1lib.actor.spazbot import (
BrawlerBotPro,
BomberBotProShielded,
)
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -222,6 +224,7 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
self._land_mine_kills = 0
self._tnt_kills = 0
@override
def on_transition_in(self) -> None:
super().on_transition_in()
customdata = bs.getsession().customdata
@ -286,6 +289,7 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
label=bs.Lstr(resource='scoreText'), score_split=0.5
)
@override
def on_begin(self) -> None:
super().on_begin()
player_count = len(self.players)
@ -825,6 +829,7 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
break
entry_count += 1
@override
def spawn_player(self, player: Player) -> bs.Actor:
# We keep track of who got hurt each wave for score purposes.
player.has_been_hurt = False
@ -1414,6 +1419,7 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
assert self._scoreboard is not None
self._scoreboard.set_team_value(self.teams[0], score, max_score=None)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, PlayerSpazHurtMessage):
msg.spaz.getplayer(Player, True).has_been_hurt = True
@ -1526,6 +1532,7 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
def _set_can_end_wave(self) -> None:
self._can_end_wave = True
@override
def end_game(self) -> None:
# Tell our bots to celebrate just to rub it in.
assert self._bots is not None
@ -1534,6 +1541,7 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
self.do_end('defeat', delay=2.0)
bs.setmusic(None)
@override
def on_continue(self) -> None:
for player in self.players:
if not player.is_alive():

View file

@ -12,11 +12,13 @@ import logging
from typing import TYPE_CHECKING
from dataclasses import dataclass
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.gameutils import SharedObjects
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -84,6 +86,7 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
label='Time', lower_is_better=True, scoretype=bs.ScoreType.MILLISECONDS
)
@override
@classmethod
def get_available_settings(
cls, sessiontype: type[bs.Session]
@ -133,10 +136,12 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
)
return settings
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.MultiTeamSession)
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
assert bs.app.classic is not None
@ -179,6 +184,7 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
bs.MusicType.EPIC_RACE if self._epic_mode else bs.MusicType.RACE
)
@override
def get_instance_description(self) -> str | Sequence:
if (
isinstance(self.session, bs.DualTeamSession)
@ -192,11 +198,13 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
return 'Run ${ARG1} laps.' + t_str, self._laps
return 'Run 1 lap.' + t_str
@override
def get_instance_description_short(self) -> str | Sequence:
if self._laps > 1:
return 'run ${ARG1} laps', self._laps
return 'run 1 lap'
@override
def on_transition_in(self) -> None:
super().on_transition_in()
shared = SharedObjects.get()
@ -379,9 +387,11 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
except Exception:
logging.exception('Error printing lap.')
@override
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
@override
def on_player_leave(self, player: Player) -> None:
super().on_player_leave(player)
@ -442,6 +452,7 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
show_value=False,
)
@override
def on_begin(self) -> None:
from bascenev1lib.actor.onscreentimer import OnScreenTimer
@ -670,6 +681,7 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
self._flash_mine(m_index)
bs.timer(0.95, bs.Call(self._make_mine, m_index))
@override
def spawn_player(self, player: Player) -> bs.Actor:
if player.team.finished:
# FIXME: This is not type-safe!
@ -758,6 +770,7 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
self.end_game()
return
@override
def end_game(self) -> None:
# Stop updating our time text, and set it to show the exact last
# finish time if we have one. (so users don't get upset if their
@ -787,6 +800,7 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
announce_winning_team=isinstance(self.session, bs.DualTeamSession),
)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
# Augment default behavior.

View file

@ -16,6 +16,9 @@ from enum import Enum
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.popuptext import PopupText
from bascenev1lib.actor.bomb import TNTSpawner
from bascenev1lib.actor.scoreboard import Scoreboard
@ -40,7 +43,6 @@ from bascenev1lib.actor.spazbot import (
BomberBotPro,
BrawlerBotPro,
)
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -194,6 +196,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
self._flawless_bonus: int | None = None
self._wave_update_timer: bs.Timer | None = None
@override
def on_transition_in(self) -> None:
super().on_transition_in()
self._scoreboard = Scoreboard(
@ -211,6 +214,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
)
)
@override
def on_begin(self) -> None:
super().on_begin()
player_count = len(self.players)
@ -571,6 +575,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
),
)
@override
def on_continue(self) -> None:
self._lives = 3
assert self._lives_text is not None
@ -578,6 +583,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
self._lives_text.node.text = str(self._lives)
self._bots.start_moving()
@override
def spawn_player(self, player: Player) -> bs.Actor:
pos = (
self._spawn_center[0] + random.uniform(-1.5, 1.5),
@ -654,6 +660,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
),
).autoretain()
@override
def end_game(self) -> None:
bs.pushcall(bs.Call(self.do_end, 'defeat'))
bs.setmusic(None)
@ -1286,6 +1293,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
# Revert to normal bot behavior otherwise..
return False
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerScoredMessage):
self._score += msg.score

View file

@ -10,11 +10,13 @@ from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.onscreencountdown import OnScreenCountdown
from bascenev1lib.actor.bomb import Bomb
from bascenev1lib.actor.popuptext import PopupText
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -49,10 +51,12 @@ class TargetPracticeGame(bs.TeamGameActivity[Player, Team]):
]
default_music = bs.MusicType.FORWARD_MARCH
@override
@classmethod
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
return ['Doom Shroom']
@override
@classmethod
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
# We support any teams or versus sessions.
@ -70,10 +74,12 @@ class TargetPracticeGame(bs.TeamGameActivity[Player, Team]):
self._enable_impact_bombs = bool(settings['Enable Impact Bombs'])
self._enable_triple_bombs = bool(settings['Enable Triple Bombs'])
@override
def on_team_join(self, team: Team) -> None:
if self.has_begun():
self.update_scoreboard()
@override
def on_begin(self) -> None:
super().on_begin()
self.update_scoreboard()
@ -86,6 +92,7 @@ class TargetPracticeGame(bs.TeamGameActivity[Player, Team]):
self._countdown = OnScreenCountdown(60, endcall=self.end_game)
bs.timer(4.0, self._countdown.start)
@override
def spawn_player(self, player: Player) -> bs.Actor:
spawn_center = (0, 3, -5)
pos = (
@ -169,6 +176,7 @@ class TargetPracticeGame(bs.TeamGameActivity[Player, Team]):
# Clear out targets that have died.
self._targets = [t for t in self._targets if t]
@override
def handlemessage(self, msg: Any) -> Any:
# When players die, respawn them.
if isinstance(msg, bs.PlayerDiedMessage):
@ -188,6 +196,7 @@ class TargetPracticeGame(bs.TeamGameActivity[Player, Team]):
for team in self.teams:
self._scoreboard.set_team_value(team, team.score)
@override
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
@ -252,9 +261,11 @@ class Target(bs.Actor):
bs.animate_array(loc3, 'size', 1, {0.1: [0.0], 0.3: [self._r3 * 2.0]})
bs.getsound('laserReverse').play()
@override
def exists(self) -> bool:
return bool(self._nodes)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
for node in self._nodes:

View file

@ -9,6 +9,9 @@ import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.bomb import TNTSpawner
from bascenev1lib.actor.scoreboard import Scoreboard
@ -29,7 +32,6 @@ from bascenev1lib.actor.spazbot import (
StickyBot,
ExplodeyBot,
)
import bascenev1 as bs
if TYPE_CHECKING:
from typing import Any, Sequence
@ -109,6 +111,7 @@ class TheLastStandGame(bs.CoopGameActivity[Player, Team]):
ExplodeyBot: SpawnInfo(0.05, 0.02, 0.002),
}
@override
def on_transition_in(self) -> None:
super().on_transition_in()
bs.timer(1.3, self._new_wave_sound.play)
@ -116,6 +119,7 @@ class TheLastStandGame(bs.CoopGameActivity[Player, Team]):
label=bs.Lstr(resource='scoreText'), score_split=0.5
)
@override
def on_begin(self) -> None:
super().on_begin()
@ -129,6 +133,7 @@ class TheLastStandGame(bs.CoopGameActivity[Player, Team]):
position=self._tntspawnpos, respawn_time=10.0
)
@override
def spawn_player(self, player: Player) -> bs.Actor:
pos = (
self._spawn_center[0] + random.uniform(-1.5, 1.5),
@ -290,6 +295,7 @@ class TheLastStandGame(bs.CoopGameActivity[Player, Team]):
assert self._scoreboard is not None
self._scoreboard.set_team_value(self.teams[0], score, max_score=None)
@override
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.PlayerDiedMessage):
player = msg.getplayer(Player)
@ -327,6 +333,7 @@ class TheLastStandGame(bs.CoopGameActivity[Player, Team]):
else:
super().handlemessage(msg)
@override
def end_game(self) -> None:
# Tell our bots to celebrate just to rub it in.
self._bots.final_celebrate()

View file

@ -10,6 +10,7 @@ import random
import weakref
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
import bauiv1 as bui
@ -42,7 +43,9 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
self._language: str | None = None
self._update_timer: bs.Timer | None = None
self._news: NewsDisplay | None = None
self._attract_mode_timer: bs.Timer | None = None
@override
def on_transition_in(self) -> None:
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements
@ -83,7 +86,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
'scale': scale,
'position': (0, 10),
'vr_depth': -10,
'text': '\xa9 2011-2023 Eric Froemling',
'text': '\xa9 2011-2024 Eric Froemling',
},
)
)
@ -295,6 +298,10 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
if not (env.demo or env.arcade) and not app.ui_v1.use_toolbars:
self._news = NewsDisplay(self)
self._attract_mode_timer = bs.Timer(
3.12, self._update_attract_mode, repeat=True
)
# Bring up the last place we were, or start at the main menu otherwise.
with bs.ContextRef.empty():
from bauiv1lib import specialoffer
@ -387,7 +394,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
bs.app.ui_v1.set_main_menu_window(
MainMenuWindow(transition=None).get_root_widget(),
from_window=None,
from_window=False, # Disable check here.
)
# attempt to show any pending offers immediately.
@ -403,6 +410,7 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
bui.apptimer(2.0, specialoffer.show_offer)
bui.apptimer(2.0, try_again)
app.classic.main_menu_did_initial_transition = True
def _update(self) -> None:
@ -836,6 +844,26 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
bui.apptimer(0.5, _start_menu_music)
def _update_attract_mode(self) -> None:
if bui.app.classic is None:
return
if not bui.app.config.resolve('Show Demos When Idle'):
return
threshold = 20.0
# If we're idle *and* have been in this activity for that long,
# flip over to our cpu demo.
if bui.get_input_idle_time() > threshold and bs.time() > threshold:
bui.app.classic.run_stress_test(
playlist_type='Random',
playlist_name='__default__',
player_count=8,
round_duration=20,
attract_mode=True,
)
class NewsDisplay:
"""Wrangles news display."""
@ -1113,6 +1141,7 @@ class MainMenuSession(bs.Session):
self._locked = False
self.setactivity(bs.newactivity(MainMenuActivity))
@override
def on_activity_end(self, activity: bs.Activity, results: Any) -> None:
if self._locked:
bui.unlock_all_input()
@ -1120,6 +1149,7 @@ class MainMenuSession(bs.Session):
# Any ending activity leads us into the main menu one.
self.setactivity(bs.newactivity(MainMenuActivity))
@override
def on_player_request(self, player: bs.SessionPlayer) -> bool:
# Reject all player requests.
return False

View file

@ -7,7 +7,9 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.gameutils import SharedObjects
if TYPE_CHECKING:
@ -22,15 +24,18 @@ class HockeyStadium(bs.Map):
name = 'Hockey Stadium'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'hockey', 'team_flag', 'keep_away']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'hockeyStadiumPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -114,15 +119,18 @@ class FootballStadium(bs.Map):
name = 'Football Stadium'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'football', 'team_flag', 'keep_away']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'footballStadiumPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -164,6 +172,7 @@ class FootballStadium(bs.Map):
gnode.vr_camera_offset = (0, -0.8, -1.1)
gnode.vr_near_clip = 0.5
@override
def is_point_near_edge(self, point: bs.Vec3, running: bool = False) -> bool:
box_position = self.defs.boxes['edge_box'][0:3]
box_scale = self.defs.boxes['edge_box'][6:9]
@ -181,16 +190,19 @@ class Bridgit(bs.Map):
name = 'Bridgit'
dataname = 'bridgit'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
# print('getting playtypes', cls._getdata()['play_types'])
return ['melee', 'team_flag', 'keep_away']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'bridgitPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -286,6 +298,7 @@ class BigG(bs.Map):
name = 'Big G'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
@ -298,10 +311,12 @@ class BigG(bs.Map):
'conquest',
]
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'bigGPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -397,15 +412,18 @@ class Roundabout(bs.Map):
name = 'Roundabout'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'roundaboutPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -502,15 +520,18 @@ class MonkeyFace(bs.Map):
name = 'Monkey Face'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'monkeyFacePreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -607,6 +628,7 @@ class ZigZag(bs.Map):
name = 'Zigzag'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
@ -618,10 +640,12 @@ class ZigZag(bs.Map):
'king_of_the_hill',
]
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'zigzagPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -715,15 +739,18 @@ class ThePad(bs.Map):
name = 'The Pad'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag', 'king_of_the_hill']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'thePadPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -804,15 +831,18 @@ class DoomShroom(bs.Map):
name = 'Doom Shroom'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'doomShroomPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -881,6 +911,7 @@ class DoomShroom(bs.Map):
gnode.vignette_outer = (0.76, 0.76, 0.76)
gnode.vignette_inner = (0.95, 0.95, 0.99)
@override
def is_point_near_edge(self, point: bs.Vec3, running: bool = False) -> bool:
xpos = point.x
zpos = point.z
@ -900,15 +931,18 @@ class LakeFrigid(bs.Map):
name = 'Lake Frigid'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag', 'race']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'lakeFrigidPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -987,15 +1021,18 @@ class TipTop(bs.Map):
name = 'Tip Top'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag', 'king_of_the_hill']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'tipTopPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -1065,15 +1102,18 @@ class CragCastle(bs.Map):
name = 'Crag Castle'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag', 'conquest']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'cragCastlePreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -1158,15 +1198,18 @@ class TowerD(bs.Map):
name = 'Tower D'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return []
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'towerDPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -1256,6 +1299,7 @@ class TowerD(bs.Map):
gnode.vignette_outer = (0.7, 0.73, 0.7)
gnode.vignette_inner = (0.95, 0.95, 0.95)
@override
def is_point_near_edge(self, point: bs.Vec3, running: bool = False) -> bool:
# see if we're within edge_box
boxes = self.defs.boxes
@ -1281,6 +1325,7 @@ class HappyThoughts(bs.Map):
name = 'Happy Thoughts'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
@ -1292,10 +1337,12 @@ class HappyThoughts(bs.Map):
'king_of_the_hill',
]
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'alwaysLandPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -1310,6 +1357,7 @@ class HappyThoughts(bs.Map):
}
return data
@override
@classmethod
def get_music_type(cls) -> bs.MusicType:
return bs.MusicType.FLYING
@ -1397,15 +1445,18 @@ class StepRightUp(bs.Map):
name = 'Step Right Up'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag', 'conquest']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'stepRightUpPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -1477,15 +1528,18 @@ class Courtyard(bs.Map):
name = 'Courtyard'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'courtyardPreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -1576,6 +1630,7 @@ class Courtyard(bs.Map):
gnode.vignette_outer = (0.6, 0.6, 0.64)
gnode.vignette_inner = (0.95, 0.95, 0.93)
@override
def is_point_near_edge(self, point: bs.Vec3, running: bool = False) -> bool:
# count anything off our ground level as safe (for our platforms)
# see if we're within edge_box
@ -1593,15 +1648,18 @@ class Rampage(bs.Map):
name = 'Rampage'
@override
@classmethod
def get_play_types(cls) -> list[str]:
"""Return valid play types for this map."""
return ['melee', 'keep_away', 'team_flag']
@override
@classmethod
def get_preview_texture_name(cls) -> str:
return 'rampagePreview'
@override
@classmethod
def on_preload(cls) -> Any:
data: dict[str, Any] = {
@ -1681,6 +1739,7 @@ class Rampage(bs.Map):
gnode.vignette_outer = (0.62, 0.64, 0.69)
gnode.vignette_inner = (0.97, 0.95, 0.93)
@override
def is_point_near_edge(self, point: bs.Vec3, running: bool = False) -> bool:
box_position = self.defs.boxes['edge_box'][0:3]
box_scale = self.defs.boxes['edge_box'][6:9]

View file

@ -19,6 +19,7 @@ import logging
from collections import deque
from typing import TYPE_CHECKING
from typing_extensions import override
import bascenev1 as bs
from bascenev1lib.actor.spaz import Spaz
@ -234,11 +235,13 @@ class TutorialActivity(bs.Activity[Player, Team]):
self._read_entries_timer: bs.Timer | None = None
self._entry_timer: bs.Timer | None = None
@override
def on_transition_in(self) -> None:
super().on_transition_in()
bs.setmusic(bs.MusicType.CHAR_SELECT, continuous=True)
self.map = self._map_type()
@override
def on_begin(self) -> None:
super().on_begin()
@ -2513,6 +2516,7 @@ class TutorialActivity(bs.Activity[Player, Team]):
self._skip_text.color = (1, 1, 1)
self._issued_warning = False
@override
def on_player_join(self, player: Player) -> None:
super().on_player_join(player)
@ -2527,6 +2531,7 @@ class TutorialActivity(bs.Activity[Player, Team]):
bs.Call(self._player_pressed_button, player),
)
@override
def on_player_leave(self, player: Player) -> None:
if not all(self.players):
logging.error(

View file

@ -47,6 +47,7 @@ from babase import (
do_once,
fade_screen,
get_display_resolution,
get_input_idle_time,
get_ip_address_type,
get_low_level_config_value,
get_max_graphics_quality,
@ -156,6 +157,7 @@ __all__ = [
'do_once',
'fade_screen',
'get_display_resolution',
'get_input_idle_time',
'get_ip_address_type',
'get_low_level_config_value',
'get_max_graphics_quality',

View file

@ -8,7 +8,9 @@ import logging
import inspect
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bauiv1
if TYPE_CHECKING:
@ -82,6 +84,7 @@ class UIV1Subsystem(babase.AppSubsystem):
"""Current ui scale for the app."""
return self._uiscale
@override
def on_app_loading(self) -> None:
from bauiv1._uitypes import UIController, ui_upkeep

View file

@ -9,6 +9,7 @@ import weakref
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing_extensions import override
import babase
import _bauiv1
@ -264,12 +265,14 @@ class TextWidgetStringEditAdapter(babase.StringEditAdapter):
description, initial_text, max_length, screen_space_center
)
@override
def _do_apply(self, new_text: str) -> None:
if self.widget:
_bauiv1.textwidget(
edit=self.widget, text=new_text, adapter_finished=True
)
@override
def _do_cancel(self) -> None:
if self.widget:
_bauiv1.textwidget(edit=self.widget, adapter_finished=True)

View file

@ -7,9 +7,11 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import logging
from bauiv1lib.popup import PopupWindow, PopupMenuWindow
from typing_extensions import override
import bauiv1 as bui
from bauiv1lib.popup import PopupWindow, PopupMenuWindow
if TYPE_CHECKING:
from typing import Any
@ -596,6 +598,7 @@ class AccountViewerWindow(PopupWindow):
self._transitioning_out = True
bui.containerwidget(edit=self.root_widget, transition='out_scale')
@override
def on_popup_cancel(self) -> None:
bui.getsound('swish').play()
self._transition_out()

View file

@ -4,6 +4,8 @@
from __future__ import annotations
from typing_extensions import override
from bauiv1lib.popup import PopupWindow
import bauiv1 as bui
@ -229,6 +231,7 @@ class AchievementsWindow(PopupWindow):
self._transitioning_out = True
bui.containerwidget(edit=self.root_widget, transition='out_scale')
@override
def on_popup_cancel(self) -> None:
bui.getsound('swish').play()
self._transition_out()

View file

@ -7,6 +7,8 @@ from __future__ import annotations
import math
from typing import TYPE_CHECKING
from typing_extensions import override
from bauiv1lib.popup import PopupWindow
import bauiv1 as bui
@ -208,6 +210,7 @@ class CharacterPicker(PopupWindow):
self._transitioning_out = True
bui.containerwidget(edit=self.root_widget, transition='out_scale')
@override
def on_popup_cancel(self) -> None:
bui.getsound('swish').play()
self._transition_out()

View file

@ -6,6 +6,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from bauiv1lib.popup import PopupWindow
import bauiv1 as bui
@ -170,6 +172,7 @@ class ColorPicker(PopupWindow):
self._delegate.color_picker_closing(self)
bui.containerwidget(edit=self.root_widget, transition='out_scale')
@override
def on_popup_cancel(self) -> None:
if not self._transitioning_out:
bui.getsound('swish').play()
@ -338,6 +341,7 @@ class ColorPickerExact(PopupWindow):
self._delegate.color_picker_closing(self)
bui.containerwidget(edit=self.root_widget, transition='out_scale')
@override
def on_popup_cancel(self) -> None:
if not self._transitioning_out:
bui.getsound('swish').play()

View file

@ -10,6 +10,8 @@ import logging
from threading import Thread
from typing import TYPE_CHECKING
from typing_extensions import override
import bauiv1 as bui
if TYPE_CHECKING:
@ -204,6 +206,7 @@ class FileSelectorWindow(bui.Window):
self._callback = callback
self._path = path
@override
def run(self) -> None:
try:
starttime = time.time()

View file

@ -6,6 +6,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing_extensions import override
from bauiv1lib.gather import GatherTab
import bauiv1 as bui
@ -16,6 +18,7 @@ if TYPE_CHECKING:
class AboutGatherTab(GatherTab):
"""The about tab in the gather UI"""
@override
def on_activate(
self,
parent_widget: bui.Widget,

View file

@ -6,13 +6,13 @@
from __future__ import annotations
import logging
from threading import Thread
from typing import TYPE_CHECKING, cast
from enum import Enum
from threading import Thread
from dataclasses import dataclass
from typing import TYPE_CHECKING, cast
from bauiv1lib.gather import GatherTab
from typing_extensions import override
import bauiv1 as bui
import bascenev1 as bs
@ -42,6 +42,7 @@ class _HostLookupThread(Thread):
self._port = port
self._call = call
@override
def run(self) -> None:
result: str | None
try:
@ -101,6 +102,7 @@ class ManualGatherTab(GatherTab):
self._party_edit_port_text: bui.Widget | None = None
self._no_parties_added_text: bui.Widget | None = None
@override
def on_activate(
self,
parent_widget: bui.Widget,
@ -180,10 +182,12 @@ class ManualGatherTab(GatherTab):
return self._container
@override
def save_state(self) -> None:
assert bui.app.classic is not None
bui.app.ui_v1.window_states[type(self)] = State(sub_tab=self._sub_tab)
@override
def restore_state(self) -> None:
assert bui.app.classic is not None
state = bui.app.ui_v1.window_states.get(type(self))
@ -771,6 +775,7 @@ class ManualGatherTab(GatherTab):
text=bui.Lstr(resource='gatherWindow.noPartiesAddedText'),
)
@override
def on_deactivate(self) -> None:
self._access_check_timer = None

View file

@ -7,10 +7,12 @@ from __future__ import annotations
import weakref
from typing import TYPE_CHECKING
from bauiv1lib.gather import GatherTab
from typing_extensions import override
import bauiv1 as bui
import bascenev1 as bs
from bauiv1lib.gather import GatherTab
if TYPE_CHECKING:
from typing import Any
@ -104,6 +106,7 @@ class NearbyGatherTab(GatherTab):
self._net_scanner: NetScanner | None = None
self._container: bui.Widget | None = None
@override
def on_activate(
self,
parent_widget: bui.Widget,
@ -156,5 +159,6 @@ class NearbyGatherTab(GatherTab):
bui.widget(edit=scrollw, autoselect=True, up_widget=tab_button)
return self._container
@override
def on_deactivate(self) -> None:
self._net_scanner = None

View file

@ -13,6 +13,7 @@ from enum import Enum
from dataclasses import dataclass
from typing import TYPE_CHECKING, cast
from typing_extensions import override
from efro.dataclassio import dataclass_from_dict, dataclass_to_dict
from bacommon.net import (
PrivateHostingState,
@ -81,6 +82,7 @@ class PrivateGatherTab(GatherTab):
logging.exception('Error building hosting config.')
self._hostingconfig = PrivateHostingConfig()
@override
def on_activate(
self,
parent_widget: bui.Widget,
@ -253,6 +255,7 @@ class PrivateGatherTab(GatherTab):
return hcfg
@override
def on_deactivate(self) -> None:
self._update_timer = None
@ -995,10 +998,12 @@ class PrivateGatherTab(GatherTab):
self._debug_server_comm('got connect response error')
bui.getsound('error').play()
@override
def save_state(self) -> None:
assert bui.app.classic is not None
bui.app.ui_v1.window_states[type(self)] = copy.deepcopy(self._state)
@override
def restore_state(self) -> None:
assert bui.app.classic is not None
state = bui.app.ui_v1.window_states.get(type(self))

Some files were not shown because too many files have changed in this diff Show more