mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2025-10-20 00:00:39 +00:00
updating ba_data to 1.7.35
This commit is contained in:
parent
d6e457c821
commit
ae30ed15ec
234 changed files with 5670 additions and 2718 deletions
10
dist/ba_data/python/babase/__init__.py
vendored
10
dist/ba_data/python/babase/__init__.py
vendored
|
|
@ -79,6 +79,11 @@ from _babase import (
|
|||
native_review_request_supported,
|
||||
native_stack_trace,
|
||||
open_file_externally,
|
||||
open_url,
|
||||
overlay_web_browser_close,
|
||||
overlay_web_browser_is_open,
|
||||
overlay_web_browser_is_supported,
|
||||
overlay_web_browser_open_url,
|
||||
print_load_info,
|
||||
pushcall,
|
||||
quit,
|
||||
|
|
@ -285,6 +290,11 @@ __all__ = [
|
|||
'normalized_color',
|
||||
'NotFoundError',
|
||||
'open_file_externally',
|
||||
'open_url',
|
||||
'overlay_web_browser_close',
|
||||
'overlay_web_browser_is_open',
|
||||
'overlay_web_browser_is_supported',
|
||||
'overlay_web_browser_open_url',
|
||||
'Permission',
|
||||
'PlayerNotFoundError',
|
||||
'Plugin',
|
||||
|
|
|
|||
99
dist/ba_data/python/babase/_app.py
vendored
99
dist/ba_data/python/babase/_app.py
vendored
|
|
@ -7,11 +7,11 @@ from __future__ import annotations
|
|||
import os
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
from typing import TYPE_CHECKING, TypeVar, override
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from functools import cached_property
|
||||
from threading import RLock
|
||||
|
||||
|
||||
from typing_extensions import override
|
||||
from efro.call import tpartial
|
||||
|
||||
import _babase
|
||||
|
|
@ -220,6 +220,13 @@ class App:
|
|||
]
|
||||
self._pool_thread_count = 0
|
||||
|
||||
# We hold a lock while lazy-loading our subsystem properties so
|
||||
# we don't spin up any subsystem more than once, but the lock is
|
||||
# recursive so that the subsystems can instantiate other
|
||||
# subsystems.
|
||||
self._subsystem_property_lock = RLock()
|
||||
self._subsystem_property_data: dict[str, AppSubsystem | bool] = {}
|
||||
|
||||
def postinit(self) -> None:
|
||||
"""Called after we've been inited and assigned to babase.app.
|
||||
|
||||
|
|
@ -227,8 +234,9 @@ class App:
|
|||
must go here instead of __init__.
|
||||
"""
|
||||
|
||||
# Hack for docs-generation: we can be imported with dummy modules
|
||||
# instead of our actual binary ones, but we don't function.
|
||||
# Hack for docs-generation: We can be imported with dummy
|
||||
# modules instead of our actual binary ones, but we don't
|
||||
# function.
|
||||
if os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') == '1':
|
||||
return
|
||||
|
||||
|
|
@ -272,10 +280,7 @@ class App:
|
|||
return self._asyncio_loop
|
||||
|
||||
def create_async_task(
|
||||
self,
|
||||
coro: Generator[Any, Any, T] | Coroutine[Any, Any, T],
|
||||
*,
|
||||
name: str | None = None,
|
||||
self, coro: Coroutine[Any, Any, T], *, name: str | None = None
|
||||
) -> None:
|
||||
"""Create a fully managed async task.
|
||||
|
||||
|
|
@ -285,6 +290,7 @@ class App:
|
|||
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
|
||||
|
|
@ -293,7 +299,6 @@ class App:
|
|||
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.
|
||||
|
|
@ -333,14 +338,66 @@ class App:
|
|||
def mode_selector(self, selector: babase.AppModeSelector) -> None:
|
||||
self._mode_selector = selector
|
||||
|
||||
def _get_subsystem_property(
|
||||
self, ssname: str, create_call: Callable[[], AppSubsystem | None]
|
||||
) -> AppSubsystem | None:
|
||||
|
||||
# Quick-out: if a subsystem is present, just return it; no
|
||||
# locking necessary.
|
||||
val = self._subsystem_property_data.get(ssname)
|
||||
if val is not None:
|
||||
if val is False:
|
||||
# False means subsystem is confirmed as unavailable.
|
||||
return None
|
||||
if val is not True:
|
||||
# A subsystem has been set. Return it.
|
||||
return val
|
||||
|
||||
# Anything else (no val present or val True) requires locking.
|
||||
with self._subsystem_property_lock:
|
||||
val = self._subsystem_property_data.get(ssname)
|
||||
if val is not None:
|
||||
if val is False:
|
||||
# False means confirmed as not present.
|
||||
return None
|
||||
if val is True:
|
||||
# True means this property is already being loaded,
|
||||
# and the fact that we're holding the lock means
|
||||
# we're doing the loading, so this is a dependency
|
||||
# loop. Not good.
|
||||
raise RuntimeError(
|
||||
f'Subsystem dependency loop detected for {ssname}'
|
||||
)
|
||||
# Must be an instantiated subsystem. Noice.
|
||||
return val
|
||||
|
||||
# Ok, there's nothing here for it. Instantiate and set it
|
||||
# while we hold the lock. Set a placeholder value of True
|
||||
# while we load so we can error if something we're loading
|
||||
# tries to recursively load us.
|
||||
self._subsystem_property_data[ssname] = True
|
||||
|
||||
# Do our one attempt to create the singleton.
|
||||
val = create_call()
|
||||
self._subsystem_property_data[ssname] = (
|
||||
False if val is None else val
|
||||
)
|
||||
|
||||
return val
|
||||
|
||||
# __FEATURESET_APP_SUBSYSTEM_PROPERTIES_BEGIN__
|
||||
# This section generated by batools.appmodule; do not edit.
|
||||
|
||||
@cached_property
|
||||
@property
|
||||
def classic(self) -> ClassicSubsystem | None:
|
||||
"""Our classic subsystem (if available)."""
|
||||
# pylint: disable=cyclic-import
|
||||
return self._get_subsystem_property(
|
||||
'classic', self._create_classic_subsystem
|
||||
) # type: ignore
|
||||
|
||||
@staticmethod
|
||||
def _create_classic_subsystem() -> ClassicSubsystem | None:
|
||||
# pylint: disable=cyclic-import
|
||||
try:
|
||||
from baclassic import ClassicSubsystem
|
||||
|
||||
|
|
@ -351,11 +408,16 @@ class App:
|
|||
logging.exception('Error importing baclassic.')
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
@property
|
||||
def plus(self) -> PlusSubsystem | None:
|
||||
"""Our plus subsystem (if available)."""
|
||||
# pylint: disable=cyclic-import
|
||||
return self._get_subsystem_property(
|
||||
'plus', self._create_plus_subsystem
|
||||
) # type: ignore
|
||||
|
||||
@staticmethod
|
||||
def _create_plus_subsystem() -> PlusSubsystem | None:
|
||||
# pylint: disable=cyclic-import
|
||||
try:
|
||||
from baplus import PlusSubsystem
|
||||
|
||||
|
|
@ -366,9 +428,15 @@ class App:
|
|||
logging.exception('Error importing baplus.')
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
@property
|
||||
def ui_v1(self) -> UIV1Subsystem:
|
||||
"""Our ui_v1 subsystem (always available)."""
|
||||
return self._get_subsystem_property(
|
||||
'ui_v1', self._create_ui_v1_subsystem
|
||||
) # type: ignore
|
||||
|
||||
@staticmethod
|
||||
def _create_ui_v1_subsystem() -> UIV1Subsystem:
|
||||
# pylint: disable=cyclic-import
|
||||
|
||||
from bauiv1 import UIV1Subsystem
|
||||
|
|
@ -384,6 +452,7 @@ class App:
|
|||
# reached the 'running' state. This ensures that all subsystems
|
||||
# receive a consistent set of callbacks starting with
|
||||
# on_app_running().
|
||||
|
||||
if self._subsystem_registration_ended:
|
||||
raise RuntimeError(
|
||||
'Subsystems can no longer be registered at this point.'
|
||||
|
|
|
|||
7
dist/ba_data/python/babase/_apputils.py
vendored
7
dist/ba_data/python/babase/_apputils.py
vendored
|
|
@ -8,9 +8,8 @@ import os
|
|||
import logging
|
||||
from threading import Thread
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
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
|
||||
|
|
@ -107,8 +106,8 @@ def handle_v1_cloud_log() -> None:
|
|||
|
||||
info = {
|
||||
'log': _babase.get_v1_cloud_log(),
|
||||
'version': app.env.version,
|
||||
'build': app.env.build_number,
|
||||
'version': app.env.engine_version,
|
||||
'build': app.env.engine_build_number,
|
||||
'userAgentString': classic.legacy_user_agent_string,
|
||||
'session': sessionname,
|
||||
'activity': activityname,
|
||||
|
|
|
|||
4
dist/ba_data/python/babase/_devconsole.py
vendored
4
dist/ba_data/python/babase/_devconsole.py
vendored
|
|
@ -4,12 +4,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from typing_extensions import override
|
||||
|
||||
import _babase
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
3
dist/ba_data/python/babase/_emptyappmode.py
vendored
3
dist/ba_data/python/babase/_emptyappmode.py
vendored
|
|
@ -3,9 +3,8 @@
|
|||
"""Provides AppMode functionality."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
from bacommon.app import AppExperience
|
||||
|
||||
import _babase
|
||||
|
|
|
|||
3
dist/ba_data/python/babase/_env.py
vendored
3
dist/ba_data/python/babase/_env.py
vendored
|
|
@ -7,9 +7,8 @@ import sys
|
|||
import signal
|
||||
import logging
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
from efro.log import LogLevel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
3
dist/ba_data/python/babase/_general.py
vendored
3
dist/ba_data/python/babase/_general.py
vendored
|
|
@ -8,9 +8,8 @@ import weakref
|
|||
import random
|
||||
import logging
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING, TypeVar, Protocol, NewType
|
||||
from typing import TYPE_CHECKING, TypeVar, Protocol, NewType, override
|
||||
|
||||
from typing_extensions import override
|
||||
from efro.terminal import Clr
|
||||
|
||||
import _babase
|
||||
|
|
|
|||
3
dist/ba_data/python/babase/_hooks.py
vendored
3
dist/ba_data/python/babase/_hooks.py
vendored
|
|
@ -85,6 +85,7 @@ def open_url_with_webbrowser_module(url: str) -> None:
|
|||
import webbrowser
|
||||
from babase._language import Lstr
|
||||
|
||||
assert _babase.in_logic_thread()
|
||||
try:
|
||||
webbrowser.open(url)
|
||||
except Exception:
|
||||
|
|
@ -384,7 +385,7 @@ def show_client_too_old_error() -> None:
|
|||
# a newer build.
|
||||
if (
|
||||
_babase.app.config.get('SuppressClientTooOldErrorForBuild')
|
||||
== _babase.app.env.build_number
|
||||
== _babase.app.env.engine_build_number
|
||||
):
|
||||
return
|
||||
|
||||
|
|
|
|||
4
dist/ba_data/python/babase/_language.py
vendored
4
dist/ba_data/python/babase/_language.py
vendored
|
|
@ -6,9 +6,7 @@ from __future__ import annotations
|
|||
import os
|
||||
import json
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, overload
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import TYPE_CHECKING, overload, override
|
||||
|
||||
import _babase
|
||||
from babase._appsubsystem import AppSubsystem
|
||||
|
|
|
|||
3
dist/ba_data/python/babase/_login.py
vendored
3
dist/ba_data/python/babase/_login.py
vendored
|
|
@ -7,9 +7,8 @@ from __future__ import annotations
|
|||
import time
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, final
|
||||
from typing import TYPE_CHECKING, final, override
|
||||
|
||||
from typing_extensions import override
|
||||
from bacommon.login import LoginType
|
||||
|
||||
import _babase
|
||||
|
|
|
|||
35
dist/ba_data/python/babase/_mgen/enums.py
vendored
35
dist/ba_data/python/babase/_mgen/enums.py
vendored
|
|
@ -1,5 +1,5 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
"""Enum vals generated by batools.pythonenumsmodule; do not edit by hand."""
|
||||
"""Enum vals generated by batools.enumspython; do not edit by hand."""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
|
@ -85,39 +85,6 @@ class UIScale(Enum):
|
|||
SMALL = 2
|
||||
|
||||
|
||||
class TimeType(Enum):
|
||||
"""Specifies the type of time for various operations to target/use.
|
||||
|
||||
Category: Enums
|
||||
|
||||
'sim' time is the local simulation time for an activity or session.
|
||||
It can proceed at different rates depending on game speed, stops
|
||||
for pauses, etc.
|
||||
|
||||
'base' is the baseline time for an activity or session. It proceeds
|
||||
consistently regardless of game speed or pausing, but may stop during
|
||||
occurrences such as network outages.
|
||||
|
||||
'real' time is mostly based on clock time, with a few exceptions. It may
|
||||
not advance while the app is backgrounded for instance. (the engine
|
||||
attempts to prevent single large time jumps from occurring)
|
||||
"""
|
||||
|
||||
SIM = 0
|
||||
BASE = 1
|
||||
REAL = 2
|
||||
|
||||
|
||||
class TimeFormat(Enum):
|
||||
"""Specifies the format time values are provided in.
|
||||
|
||||
Category: Enums
|
||||
"""
|
||||
|
||||
SECONDS = 0
|
||||
MILLISECONDS = 1
|
||||
|
||||
|
||||
class Permission(Enum):
|
||||
"""Permissions that can be requested from the OS.
|
||||
|
||||
|
|
|
|||
2
dist/ba_data/python/babase/_net.py
vendored
2
dist/ba_data/python/babase/_net.py
vendored
|
|
@ -32,6 +32,8 @@ class NetworkSubsystem:
|
|||
# For debugging.
|
||||
self.v1_test_log: str = ''
|
||||
self.v1_ctest_results: dict[int, str] = {}
|
||||
self.connectivity_state = 'uninited'
|
||||
self.transport_state = 'uninited'
|
||||
self.server_time_offset_hours: float | None = None
|
||||
|
||||
@property
|
||||
|
|
|
|||
4
dist/ba_data/python/babase/_plugin.py
vendored
4
dist/ba_data/python/babase/_plugin.py
vendored
|
|
@ -6,9 +6,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
import importlib.util
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
import _babase
|
||||
from babase._appsubsystem import AppSubsystem
|
||||
|
|
|
|||
4
dist/ba_data/python/babase/_ui.py
vendored
4
dist/ba_data/python/babase/_ui.py
vendored
|
|
@ -3,9 +3,7 @@
|
|||
"""UI related bits of babase."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from babase._stringedit import StringEditAdapter
|
||||
import _babase
|
||||
|
|
|
|||
22
dist/ba_data/python/babase/modutils.py
vendored
22
dist/ba_data/python/babase/modutils.py
vendored
|
|
@ -133,10 +133,15 @@ def create_user_system_scripts() -> None:
|
|||
if env.python_directory_app is None:
|
||||
raise RuntimeError('app python dir unset')
|
||||
|
||||
path = f'{env.python_directory_user}/sys/{env.version}'
|
||||
path = f'{env.python_directory_user}/sys/{env.engine_version}'
|
||||
pathtmp = path + '_tmp'
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
print('Delete Existing User Scripts first!')
|
||||
_babase.screenmessage(
|
||||
'Delete Existing User Scripts first!',
|
||||
color=(1, 0, 0),
|
||||
)
|
||||
return
|
||||
if os.path.exists(pathtmp):
|
||||
shutil.rmtree(pathtmp)
|
||||
|
||||
|
|
@ -159,6 +164,7 @@ def create_user_system_scripts() -> None:
|
|||
f"'\nRestart {_babase.appname()} to use them."
|
||||
f' (use babase.quit() to exit the game)'
|
||||
)
|
||||
_babase.screenmessage('Created User System Scripts', color=(0, 1, 0))
|
||||
if app.classic is not None and app.classic.platform == 'android':
|
||||
print(
|
||||
'Note: the new files may not be visible via '
|
||||
|
|
@ -175,16 +181,18 @@ def delete_user_system_scripts() -> None:
|
|||
if env.python_directory_user is None:
|
||||
raise RuntimeError('user python dir unset')
|
||||
|
||||
path = f'{env.python_directory_user}/sys/{env.version}'
|
||||
path = f'{env.python_directory_user}/sys/{env.engine_version}'
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
print(
|
||||
f'User system scripts deleted.\n'
|
||||
f'Restart {_babase.appname()} to use internal'
|
||||
f' scripts. (use babase.quit() to exit the game)'
|
||||
print('User system scripts deleted.')
|
||||
_babase.screenmessage('Deleted User System Scripts', color=(0, 1, 0))
|
||||
_babase.screenmessage(
|
||||
f'Closing {_babase.appname()} to make changes.', color=(0, 1, 0)
|
||||
)
|
||||
_babase.apptimer(2.0, _babase.quit)
|
||||
else:
|
||||
print(f"User system scripts not found at '{path}'.")
|
||||
_babase.screenmessage('User Scripts Not Found', color=(1, 0, 0))
|
||||
|
||||
# If the sys path is empty, kill it.
|
||||
dpath = env.python_directory_user + '/sys'
|
||||
|
|
|
|||
6
dist/ba_data/python/baclassic/_accountv1.py
vendored
6
dist/ba_data/python/baclassic/_accountv1.py
vendored
|
|
@ -152,9 +152,9 @@ class AccountV1Subsystem:
|
|||
"""(internal)"""
|
||||
|
||||
for entry in info:
|
||||
cache_entry = self.tournament_info[
|
||||
entry['tournamentID']
|
||||
] = copy.deepcopy(entry)
|
||||
cache_entry = self.tournament_info[entry['tournamentID']] = (
|
||||
copy.deepcopy(entry)
|
||||
)
|
||||
|
||||
# Also store the time we received this, so we can adjust
|
||||
# time-remaining values/etc.
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ class AchievementSubsystem:
|
|||
|
||||
def __init__(self) -> None:
|
||||
self.achievements: list[Achievement] = []
|
||||
self.achievements_to_display: (
|
||||
list[tuple[baclassic.Achievement, bool]]
|
||||
) = []
|
||||
self.achievements_to_display: list[
|
||||
tuple[baclassic.Achievement, bool]
|
||||
] = []
|
||||
self.achievement_display_timer: bascenev1.BaseTimer | None = None
|
||||
self.last_achievement_display_time: float = 0.0
|
||||
self.achievement_completion_banner_slots: set[int] = set()
|
||||
|
|
|
|||
3
dist/ba_data/python/baclassic/_benchmark.py
vendored
3
dist/ba_data/python/baclassic/_benchmark.py
vendored
|
|
@ -5,9 +5,8 @@ from __future__ import annotations
|
|||
|
||||
import random
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
import bascenev1
|
||||
import _baclassic
|
||||
|
|
|
|||
10
dist/ba_data/python/baclassic/_input.py
vendored
10
dist/ba_data/python/baclassic/_input.py
vendored
|
|
@ -13,7 +13,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
def get_input_device_mapped_value(
|
||||
devicename: str, unique_id: str, name: str
|
||||
devicename: str,
|
||||
unique_id: str,
|
||||
name: str,
|
||||
default: bool = False,
|
||||
) -> Any:
|
||||
"""Returns a mapped value for an input device.
|
||||
|
||||
|
|
@ -30,8 +33,9 @@ def get_input_device_mapped_value(
|
|||
subplatform = app.classic.subplatform
|
||||
appconfig = babase.app.config
|
||||
|
||||
# If there's an entry in our config for this controller, use it.
|
||||
if 'Controllers' in appconfig:
|
||||
# If there's an entry in our config for this controller and
|
||||
# we're not looking for our default mappings, use it.
|
||||
if 'Controllers' in appconfig and not default:
|
||||
ccfgs = appconfig['Controllers']
|
||||
if devicename in ccfgs:
|
||||
mapping = None
|
||||
|
|
|
|||
3
dist/ba_data/python/baclassic/_net.py
vendored
3
dist/ba_data/python/baclassic/_net.py
vendored
|
|
@ -7,9 +7,8 @@ import copy
|
|||
import weakref
|
||||
import threading
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
import bascenev1
|
||||
|
||||
|
|
|
|||
47
dist/ba_data/python/baclassic/_servermode.py
vendored
47
dist/ba_data/python/baclassic/_servermode.py
vendored
|
|
@ -102,8 +102,8 @@ class ServerController:
|
|||
self._shutdown_reason: ShutdownReason | None = None
|
||||
self._executing_shutdown = False
|
||||
|
||||
# Make note if they want us to import a playlist;
|
||||
# we'll need to do that first if so.
|
||||
# Make note if they want us to import a playlist; we'll need to
|
||||
# do that first if so.
|
||||
self._playlist_fetch_running = self._config.playlist_code is not None
|
||||
self._playlist_fetch_sent_request = False
|
||||
self._playlist_fetch_got_response = False
|
||||
|
|
@ -216,7 +216,7 @@ class ServerController:
|
|||
'bsAccessCheck',
|
||||
{
|
||||
'port': bascenev1.get_game_port(),
|
||||
'b': babase.app.env.build_number,
|
||||
'b': babase.app.env.engine_build_number,
|
||||
},
|
||||
callback=self._access_check_response,
|
||||
)
|
||||
|
|
@ -304,7 +304,7 @@ class ServerController:
|
|||
) -> None:
|
||||
if result is None:
|
||||
print('Error fetching playlist; aborting.')
|
||||
print('Falling back to use default playlist.') #BCS
|
||||
print('Falling back to use default playlist.')
|
||||
self._config.session_type = "teams"
|
||||
self._prep_timer = None
|
||||
babase.pushcall(self._launch_server_session)
|
||||
|
|
@ -314,9 +314,7 @@ class ServerController:
|
|||
typename = (
|
||||
'teams'
|
||||
if result['playlistType'] == 'Team Tournament'
|
||||
else 'ffa'
|
||||
if result['playlistType'] == 'Free-for-All'
|
||||
else '??'
|
||||
else 'ffa' if result['playlistType'] == 'Free-for-All' else '??'
|
||||
)
|
||||
plistname = result['playlistName']
|
||||
print(f'{Clr.SBLU}Got playlist: "{plistname}" ({typename}).{Clr.RST}')
|
||||
|
|
@ -372,7 +370,8 @@ class ServerController:
|
|||
raise RuntimeError(f'Unknown session type {sessiontype}')
|
||||
|
||||
# Need to add this in a transaction instead of just setting
|
||||
# it directly or it will get overwritten by the master-server.
|
||||
# it directly or it will get overwritten by the
|
||||
# master-server.
|
||||
plus.add_v1_account_transaction(
|
||||
{
|
||||
'type': 'ADD_PLAYLIST',
|
||||
|
|
@ -386,22 +385,23 @@ class ServerController:
|
|||
if self._first_run:
|
||||
curtimestr = time.strftime('%c')
|
||||
startupmsg = (
|
||||
f'{Clr.BLD}{Clr.BLU}{babase.appnameupper()} {app.env.version}'
|
||||
f' ({app.env.build_number})'
|
||||
f'{Clr.BLD}{Clr.BLU}{babase.appnameupper()}'
|
||||
f' {app.env.engine_version}'
|
||||
f' ({app.env.engine_build_number})'
|
||||
f' entering server-mode {curtimestr}{Clr.RST}'
|
||||
)
|
||||
logging.info(startupmsg)
|
||||
|
||||
if sessiontype is bascenev1.FreeForAllSession:
|
||||
appcfg['Free-for-All Playlist Selection'] = self._playlist_name
|
||||
appcfg[
|
||||
'Free-for-All Playlist Randomize'
|
||||
] = self._config.playlist_shuffle
|
||||
appcfg['Free-for-All Playlist Randomize'] = (
|
||||
self._config.playlist_shuffle
|
||||
)
|
||||
elif sessiontype is bascenev1.DualTeamSession:
|
||||
appcfg['Team Tournament Playlist Selection'] = self._playlist_name
|
||||
appcfg[
|
||||
'Team Tournament Playlist Randomize'
|
||||
] = self._config.playlist_shuffle
|
||||
appcfg['Team Tournament Playlist Randomize'] = (
|
||||
self._config.playlist_shuffle
|
||||
)
|
||||
elif sessiontype is bascenev1.CoopSession:
|
||||
classic.coop_session_args = {
|
||||
'campaign': self._config.coop_campaign,
|
||||
|
|
@ -410,6 +410,10 @@ class ServerController:
|
|||
else:
|
||||
raise RuntimeError(f'Unknown session type {sessiontype}')
|
||||
|
||||
appcfg['Teams Series Length'] = self._config.teams_series_length
|
||||
appcfg['FFA Series Length'] = self._config.ffa_series_length
|
||||
|
||||
# Deprecated; left here in order to not break mods.
|
||||
classic.teams_series_length = self._config.teams_series_length
|
||||
classic.ffa_series_length = self._config.ffa_series_length
|
||||
|
||||
|
|
@ -425,12 +429,23 @@ class ServerController:
|
|||
bascenev1.set_public_party_queue_enabled(self._config.enable_queue)
|
||||
bascenev1.set_public_party_name(self._config.party_name)
|
||||
bascenev1.set_public_party_stats_url(self._config.stats_url)
|
||||
bascenev1.set_public_party_public_address_ipv4(
|
||||
self._config.public_ipv4_address
|
||||
)
|
||||
bascenev1.set_public_party_public_address_ipv6(
|
||||
self._config.public_ipv6_address
|
||||
)
|
||||
|
||||
bascenev1.set_public_party_enabled(self._config.party_is_public)
|
||||
|
||||
bascenev1.set_player_rejoin_cooldown(
|
||||
self._config.player_rejoin_cooldown
|
||||
)
|
||||
|
||||
bascenev1.set_max_players_override(
|
||||
self._config.session_max_players_override
|
||||
)
|
||||
|
||||
# And here.. we.. go.
|
||||
if self._config.stress_test_players is not None:
|
||||
# Special case: run a stress test.
|
||||
|
|
|
|||
8
dist/ba_data/python/baclassic/_store.py
vendored
8
dist/ba_data/python/baclassic/_store.py
vendored
|
|
@ -7,6 +7,8 @@ from __future__ import annotations
|
|||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from efro.util import utc_now
|
||||
|
||||
import babase
|
||||
import bascenev1
|
||||
|
||||
|
|
@ -522,10 +524,10 @@ class StoreSubsystem:
|
|||
if item in sales_raw:
|
||||
if not plus.get_purchased(item):
|
||||
to_end = (
|
||||
datetime.datetime.utcfromtimestamp(
|
||||
sales_raw[item]['e']
|
||||
datetime.datetime.fromtimestamp(
|
||||
sales_raw[item]['e'], datetime.UTC
|
||||
)
|
||||
- datetime.datetime.utcnow()
|
||||
- utc_now()
|
||||
).total_seconds()
|
||||
if to_end > 0:
|
||||
sale_times.append(int(to_end * 1000))
|
||||
|
|
|
|||
16
dist/ba_data/python/baclassic/_subsystem.py
vendored
16
dist/ba_data/python/baclassic/_subsystem.py
vendored
|
|
@ -3,12 +3,11 @@
|
|||
"""Provides classic app subsystem."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
import random
|
||||
import logging
|
||||
import weakref
|
||||
|
||||
from typing_extensions import override
|
||||
from efro.dataclassio import dataclass_from_dict
|
||||
import babase
|
||||
import bauiv1
|
||||
|
|
@ -103,8 +102,8 @@ class ClassicSubsystem(babase.AppSubsystem):
|
|||
self.maps: dict[str, type[bascenev1.Map]] = {}
|
||||
|
||||
# Gameplay.
|
||||
self.teams_series_length = 7
|
||||
self.ffa_series_length = 24
|
||||
self.teams_series_length = 7 # deprecated, left for old mods
|
||||
self.ffa_series_length = 24 # deprecated, left for old mods
|
||||
self.coop_session_args: dict = {}
|
||||
|
||||
# UI.
|
||||
|
|
@ -575,15 +574,18 @@ class ClassicSubsystem(babase.AppSubsystem):
|
|||
)
|
||||
|
||||
def get_input_device_mapped_value(
|
||||
self, device: bascenev1.InputDevice, name: str
|
||||
self,
|
||||
device: bascenev1.InputDevice,
|
||||
name: str,
|
||||
default: bool = False,
|
||||
) -> Any:
|
||||
"""Returns a mapped value for an input device.
|
||||
"""Return a mapped value for an input device.
|
||||
|
||||
This checks the user config and falls back to default values
|
||||
where available.
|
||||
"""
|
||||
return _input.get_input_device_mapped_value(
|
||||
device.name, device.unique_identifier, name
|
||||
device.name, device.unique_identifier, name, default
|
||||
)
|
||||
|
||||
def get_input_device_map_hash(
|
||||
|
|
|
|||
8
dist/ba_data/python/baclassic/_tournament.py
vendored
8
dist/ba_data/python/baclassic/_tournament.py
vendored
|
|
@ -35,9 +35,11 @@ def get_tournament_prize_strings(entry: dict[str, Any]) -> list[str]:
|
|||
prval = (
|
||||
''
|
||||
if rng is None
|
||||
else ('#' + str(rng[0]))
|
||||
if (rng[0] == rng[1])
|
||||
else ('#' + str(rng[0]) + '-' + str(rng[1]))
|
||||
else (
|
||||
('#' + str(rng[0]))
|
||||
if (rng[0] == rng[1])
|
||||
else ('#' + str(rng[0]) + '-' + str(rng[1]))
|
||||
)
|
||||
)
|
||||
pvval = ''
|
||||
if trophy_type is not None:
|
||||
|
|
|
|||
3
dist/ba_data/python/baclassic/macmusicapp.py
vendored
3
dist/ba_data/python/baclassic/macmusicapp.py
vendored
|
|
@ -6,9 +6,8 @@ from __future__ import annotations
|
|||
import logging
|
||||
import threading
|
||||
from collections import deque
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
from baclassic._music import MusicPlayer
|
||||
|
|
|
|||
3
dist/ba_data/python/baclassic/osmusic.py
vendored
3
dist/ba_data/python/baclassic/osmusic.py
vendored
|
|
@ -7,9 +7,8 @@ import os
|
|||
import random
|
||||
import logging
|
||||
import threading
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
from baclassic._music import MusicPlayer
|
||||
|
|
|
|||
24
dist/ba_data/python/bacommon/app.py
vendored
24
dist/ba_data/python/bacommon/app.py
vendored
|
|
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class AppInterfaceIdiom(Enum):
|
||||
"""A general form-factor or way of experiencing a Ballistica app.
|
||||
"""A general form-factor or method of experiencing a Ballistica app.
|
||||
|
||||
Note that it is possible for a running app to switch idioms (for
|
||||
instance if a mobile device or computer is connected to a TV).
|
||||
|
|
@ -32,11 +32,11 @@ class AppExperience(Enum):
|
|||
"""A particular experience that can be provided by a Ballistica app.
|
||||
|
||||
This is one metric used to isolate different playerbases from
|
||||
eachother where there might be no technical barriers doing so.
|
||||
For example, a casual one-hand-playable phone game and an augmented
|
||||
each other where there might be no technical barriers doing so. For
|
||||
example, a casual one-hand-playable phone game and an augmented
|
||||
reality tabletop game may both use the same scene-versions and
|
||||
networking-protocols and whatnot, but it would make no sense to
|
||||
allow players of one join servers for the other. AppExperience can
|
||||
allow players of one to join servers of the other. AppExperience can
|
||||
be used to keep these player bases separate.
|
||||
|
||||
Generally a single Ballistica app targets a single AppExperience.
|
||||
|
|
@ -47,13 +47,14 @@ class AppExperience(Enum):
|
|||
visible to client apps designed for that play style.
|
||||
"""
|
||||
|
||||
# An experience that is supported everywhere. Used
|
||||
# for the default empty AppMode when starting the app, etc.
|
||||
# An experience that is supported everywhere. Used for the default
|
||||
# empty AppMode when starting the app, etc.
|
||||
EMPTY = 'empty'
|
||||
|
||||
# The traditional BombSquad experience: multiple players using
|
||||
# controllers in a single arena small enough for all action to be
|
||||
# viewed on a single screen.
|
||||
# traditional game controllers (or touch screen equivalents) in a
|
||||
# single arena small enough for all action to be viewed on a single
|
||||
# screen.
|
||||
MELEE = 'melee'
|
||||
|
||||
# The traditional BombSquad Remote experience; buttons on a
|
||||
|
|
@ -72,7 +73,7 @@ class AppArchitecture(Enum):
|
|||
|
||||
|
||||
class AppPlatform(Enum):
|
||||
"""Overall platform a Ballistica build can be targeting.
|
||||
"""Overall platform a Ballistica build is targeting.
|
||||
|
||||
Each distinct flavor of an app has a unique combination
|
||||
of AppPlatform and AppVariant. Generally platform describes
|
||||
|
|
@ -124,8 +125,9 @@ class AppInstanceInfo:
|
|||
"""General info about an individual running app."""
|
||||
|
||||
name = Annotated[str, IOAttrs('n')]
|
||||
version = Annotated[str, IOAttrs('v')]
|
||||
build = Annotated[int, IOAttrs('b')]
|
||||
|
||||
engine_version = Annotated[str, IOAttrs('ev')]
|
||||
engine_build = Annotated[int, IOAttrs('eb')]
|
||||
|
||||
platform = Annotated[AppPlatform, IOAttrs('p')]
|
||||
variant = Annotated[AppVariant, IOAttrs('va')]
|
||||
|
|
|
|||
23
dist/ba_data/python/bacommon/cloud.py
vendored
23
dist/ba_data/python/bacommon/cloud.py
vendored
|
|
@ -4,10 +4,9 @@
|
|||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING, Annotated
|
||||
from typing import TYPE_CHECKING, Annotated, override
|
||||
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
|
||||
|
|
@ -33,9 +32,12 @@ class LoginProxyRequestMessage(Message):
|
|||
class LoginProxyRequestResponse(Response):
|
||||
"""Response to a request for a login proxy."""
|
||||
|
||||
# URL to direct the user to for login.
|
||||
# URL to direct the user to for sign in.
|
||||
url: Annotated[str, IOAttrs('u')]
|
||||
|
||||
# URL to use for overlay-web-browser sign ins.
|
||||
url_overlay: Annotated[str, IOAttrs('uo')]
|
||||
|
||||
# Proxy-Login id for querying results.
|
||||
proxyid: Annotated[str, IOAttrs('p')]
|
||||
|
||||
|
|
@ -123,24 +125,25 @@ class TestResponse(Response):
|
|||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class PromoCodeMessage(Message):
|
||||
"""User is entering a promo code"""
|
||||
class SendInfoMessage(Message):
|
||||
"""User is using the send-info function"""
|
||||
|
||||
code: Annotated[str, IOAttrs('c')]
|
||||
description: Annotated[str, IOAttrs('c')]
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def get_response_types(cls) -> list[type[Response] | None]:
|
||||
return [PromoCodeResponse]
|
||||
return [SendInfoResponse]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class PromoCodeResponse(Response):
|
||||
"""Applied that promo code for ya, boss."""
|
||||
class SendInfoResponse(Response):
|
||||
"""Response to sending into the server."""
|
||||
|
||||
valid: Annotated[bool, IOAttrs('v')]
|
||||
handled: Annotated[bool, IOAttrs('v')]
|
||||
message: Annotated[str | None, IOAttrs('m', store_default=False)] = None
|
||||
legacy_code: Annotated[str | None, IOAttrs('l', store_default=False)] = None
|
||||
|
||||
|
||||
@ioprepped
|
||||
|
|
|
|||
18
dist/ba_data/python/bacommon/net.py
vendored
18
dist/ba_data/python/bacommon/net.py
vendored
|
|
@ -20,6 +20,11 @@ class ServerNodeEntry:
|
|||
"""Information about a specific server."""
|
||||
|
||||
zone: Annotated[str, IOAttrs('r')]
|
||||
|
||||
# TODO: Remove soft_default after all master-servers upgraded.
|
||||
latlong: Annotated[
|
||||
tuple[float, float] | None, IOAttrs('ll', soft_default=None)
|
||||
]
|
||||
address: Annotated[str, IOAttrs('a')]
|
||||
port: Annotated[int, IOAttrs('p')]
|
||||
|
||||
|
|
@ -32,6 +37,16 @@ class ServerNodeQueryResponse:
|
|||
# The current utc time on the master server.
|
||||
time: Annotated[datetime.datetime, IOAttrs('t')]
|
||||
|
||||
# Where the master server sees the query as coming from.
|
||||
latlong: Annotated[tuple[float, float] | None, IOAttrs('ll')]
|
||||
|
||||
ping_per_dist: Annotated[float, IOAttrs('ppd')]
|
||||
max_dist: Annotated[float, IOAttrs('md')]
|
||||
|
||||
debug_log_seconds: Annotated[
|
||||
float | None, IOAttrs('d', store_default=False)
|
||||
] = None
|
||||
|
||||
# If present, something went wrong, and this describes it.
|
||||
error: Annotated[str | None, IOAttrs('e', store_default=False)] = None
|
||||
|
||||
|
|
@ -78,6 +93,7 @@ class PrivatePartyConnectResult:
|
|||
"""Info about a server we get back when connecting."""
|
||||
|
||||
error: str | None = None
|
||||
addr: str | None = None
|
||||
address4: Annotated[str | None, IOAttrs('addr')] = None
|
||||
address6: Annotated[str | None, IOAttrs('addr6')] = None
|
||||
port: int | None = None
|
||||
password: str | None = None
|
||||
|
|
|
|||
153
dist/ba_data/python/bacommon/servermanager.py
vendored
153
dist/ba_data/python/bacommon/servermanager.py
vendored
|
|
@ -22,109 +22,138 @@ class ServerConfig:
|
|||
party_name: str = 'FFA'
|
||||
|
||||
# If True, your party will show up in the global public party list
|
||||
# Otherwise it will still be joinable via LAN or connecting by IP address.
|
||||
# Otherwise it will still be joinable via LAN or connecting by IP
|
||||
# address.
|
||||
party_is_public: bool = True
|
||||
|
||||
# If True, all connecting clients will be authenticated through the master
|
||||
# server to screen for fake account info. Generally this should always
|
||||
# be enabled unless you are hosting on a LAN with no internet connection.
|
||||
# If True, all connecting clients will be authenticated through the
|
||||
# master server to screen for fake account info. Generally this
|
||||
# should always be enabled unless you are hosting on a LAN with no
|
||||
# internet connection.
|
||||
authenticate_clients: bool = True
|
||||
|
||||
# IDs of server admins. Server admins are not kickable through the default
|
||||
# kick vote system and they are able to kick players without a vote. To get
|
||||
# your account id, enter 'getaccountid' in settings->advanced->enter-code.
|
||||
# IDs of server admins. Server admins are not kickable through the
|
||||
# default kick vote system and they are able to kick players without
|
||||
# a vote. To get your account id, enter 'getaccountid' in
|
||||
# settings->advanced->enter-code.
|
||||
admins: list[str] = field(default_factory=list)
|
||||
|
||||
# Whether the default kick-voting system is enabled.
|
||||
enable_default_kick_voting: bool = True
|
||||
|
||||
# UDP port to host on. Change this to work around firewalls or run multiple
|
||||
# servers on one machine.
|
||||
# 43210 is the default and the only port that will show up in the LAN
|
||||
# browser tab.
|
||||
# To be included in the public server list, your server MUST be
|
||||
# accessible via an ipv4 address. By default, the master server will
|
||||
# try to use the address your server contacts it from, but this may
|
||||
# be an ipv6 address these days so you may need to provide an ipv4
|
||||
# address explicitly.
|
||||
public_ipv4_address: str | None = None
|
||||
|
||||
# You can optionally provide an ipv6 address for your server for the
|
||||
# public server list. Unlike ipv4, a server is not required to have
|
||||
# an ipv6 address to appear in the list, but is still good to
|
||||
# provide when available since more and more devices are using ipv6
|
||||
# these days. Your server's ipv6 address will be autodetected if
|
||||
# your server uses ipv6 when communicating with the master server. You
|
||||
# can pass an empty string here to explicitly disable the ipv6
|
||||
# address.
|
||||
public_ipv6_address: str | None = None
|
||||
|
||||
# UDP port to host on. Change this to work around firewalls or run
|
||||
# multiple servers on one machine.
|
||||
#
|
||||
# 43210 is the default and the only port that will show up in the
|
||||
# LAN browser tab.
|
||||
port: int = 43210
|
||||
|
||||
# Max devices in the party. Note that this does *NOT* mean max players.
|
||||
# Any device in the party can have more than one player on it if they have
|
||||
# multiple controllers. Also, this number currently includes the server so
|
||||
# generally make it 1 bigger than you need. Max-players is not currently
|
||||
# exposed but I'll try to add that soon.
|
||||
# Max devices in the party. Note that this does *NOT* mean max
|
||||
# players. Any device in the party can have more than one player on
|
||||
# it if they have multiple controllers. Also, this number currently
|
||||
# includes the server so generally make it 1 bigger than you need.
|
||||
max_party_size: int = 6
|
||||
|
||||
# Options here are 'ffa' (free-for-all), 'teams' and 'coop' (cooperative)
|
||||
# This value is ignored if you supply a playlist_code (see below).
|
||||
# Max players that can join a session. If present this will override
|
||||
# the session's preferred max_players. if a value below 0 is given
|
||||
# player limit will be removed.
|
||||
session_max_players_override: int | None = None
|
||||
|
||||
# Options here are 'ffa' (free-for-all), 'teams' and 'coop'
|
||||
# (cooperative) This value is ignored if you supply a playlist_code
|
||||
# (see below).
|
||||
session_type: str = 'ffa'
|
||||
|
||||
# Playlist-code for teams or free-for-all mode sessions.
|
||||
# To host your own custom playlists, use the 'share' functionality in the
|
||||
# playlist editor in the regular version of the game.
|
||||
# This will give you a numeric code you can enter here to host that
|
||||
# playlist.
|
||||
# Playlist-code for teams or free-for-all mode sessions. To host
|
||||
# your own custom playlists, use the 'share' functionality in the
|
||||
# playlist editor in the regular version of the game. This will give
|
||||
# you a numeric code you can enter here to host that playlist.
|
||||
playlist_code: int | None = None
|
||||
|
||||
# Alternately, you can embed playlist data here instead of using codes.
|
||||
# Make sure to set session_type to the correct type for the data here.
|
||||
# Alternately, you can embed playlist data here instead of using
|
||||
# codes. Make sure to set session_type to the correct type for the
|
||||
# data here.
|
||||
playlist_inline: list[dict[str, Any]] | None = None
|
||||
|
||||
# Whether to shuffle the playlist or play its games in designated order.
|
||||
# Whether to shuffle the playlist or play its games in designated
|
||||
# order.
|
||||
playlist_shuffle: bool = True
|
||||
|
||||
# If True, keeps team sizes equal by disallowing joining the largest team
|
||||
# (teams mode only).
|
||||
# If True, keeps team sizes equal by disallowing joining the largest
|
||||
# team (teams mode only).
|
||||
auto_balance_teams: bool = True
|
||||
|
||||
# The campaign used when in co-op session mode.
|
||||
# Do print(ba.app.campaigns) to see available campaign names.
|
||||
# The campaign used when in co-op session mode. Do
|
||||
# print(ba.app.campaigns) to see available campaign names.
|
||||
coop_campaign: str = 'Easy'
|
||||
|
||||
# The level name within the campaign used in co-op session mode.
|
||||
# For campaign name FOO, do print(ba.app.campaigns['FOO'].levels) to see
|
||||
# The level name within the campaign used in co-op session mode. For
|
||||
# campaign name FOO, do print(ba.app.campaigns['FOO'].levels) to see
|
||||
# available level names.
|
||||
coop_level: str = 'Onslaught Training'
|
||||
|
||||
# Whether to enable telnet access.
|
||||
# IMPORTANT: This option is no longer available, as it was being used
|
||||
# for exploits. Live access to the running server is still possible through
|
||||
# the mgr.cmd() function in the server script. Run your server through
|
||||
# tools such as 'screen' or 'tmux' and you can reconnect to it remotely
|
||||
# over a secure ssh connection.
|
||||
#
|
||||
# IMPORTANT: This option is no longer available, as it was being
|
||||
# used for exploits. Live access to the running server is still
|
||||
# possible through the mgr.cmd() function in the server script. Run
|
||||
# your server through tools such as 'screen' or 'tmux' and you can
|
||||
# reconnect to it remotely over a secure ssh connection.
|
||||
enable_telnet: bool = False
|
||||
|
||||
# Series length in teams mode (7 == 'best-of-7' series; a team must
|
||||
# get 4 wins)
|
||||
teams_series_length: int = 7
|
||||
|
||||
# Points to win in free-for-all mode (Points are awarded per game based on
|
||||
# performance)
|
||||
# Points to win in free-for-all mode (Points are awarded per game
|
||||
# based on performance)
|
||||
ffa_series_length: int = 24
|
||||
|
||||
# If you have a custom stats webpage for your server, you can use this
|
||||
# to provide a convenient in-game link to it in the server-browser
|
||||
# alongside the server name.
|
||||
# If you have a custom stats webpage for your server, you can use
|
||||
# this to provide a convenient in-game link to it in the
|
||||
# server-browser alongside the server name.
|
||||
#
|
||||
# if ${ACCOUNT} is present in the string, it will be replaced by the
|
||||
# currently-signed-in account's id. To fetch info about an account,
|
||||
# your back-end server can use the following url:
|
||||
# https://legacy.ballistica.net/accountquery?id=ACCOUNT_ID_HERE
|
||||
stats_url: str | None = None
|
||||
|
||||
# If present, the server subprocess will attempt to gracefully exit after
|
||||
# this amount of time. A graceful exit can occur at the end of a series
|
||||
# or other opportune time. Server-managers set to auto-restart (the
|
||||
# default) will then spin up a fresh subprocess. This mechanism can be
|
||||
# useful to clear out any memory leaks or other accumulated bad state
|
||||
# in the server subprocess.
|
||||
# If present, the server subprocess will attempt to gracefully exit
|
||||
# after this amount of time. A graceful exit can occur at the end of
|
||||
# a series or other opportune time. Server-managers set to
|
||||
# auto-restart (the default) will then spin up a fresh subprocess.
|
||||
# This mechanism can be useful to clear out any memory leaks or
|
||||
# other accumulated bad state in the server subprocess.
|
||||
clean_exit_minutes: float | None = None
|
||||
|
||||
# If present, the server subprocess will shut down immediately after this
|
||||
# amount of time. This can be useful as a fallback for clean_exit_time.
|
||||
# The server manager will then spin up a fresh server subprocess if
|
||||
# auto-restart is enabled (the default).
|
||||
# If present, the server subprocess will shut down immediately after
|
||||
# this amount of time. This can be useful as a fallback for
|
||||
# clean_exit_time. The server manager will then spin up a fresh
|
||||
# server subprocess if auto-restart is enabled (the default).
|
||||
unclean_exit_minutes: float | None = None
|
||||
|
||||
# If present, the server subprocess will shut down immediately if this
|
||||
# amount of time passes with no activity from any players. The server
|
||||
# manager will then spin up a fresh server subprocess if auto-restart is
|
||||
# enabled (the default).
|
||||
# If present, the server subprocess will shut down immediately if
|
||||
# this amount of time passes with no activity from any players. The
|
||||
# server manager will then spin up a fresh server subprocess if
|
||||
# auto-restart is enabled (the default).
|
||||
idle_exit_minutes: float | None = None
|
||||
|
||||
# Should the tutorial be shown at the beginning of games?
|
||||
|
|
@ -138,9 +167,9 @@ class ServerConfig:
|
|||
tuple[tuple[float, float, float], tuple[float, float, float]] | None
|
||||
) = None
|
||||
|
||||
# Whether to enable the queue where players can line up before entering
|
||||
# your server. Disabling this can be used as a workaround to deal with
|
||||
# queue spamming attacks.
|
||||
# Whether to enable the queue where players can line up before
|
||||
# entering your server. Disabling this can be used as a workaround
|
||||
# to deal with queue spamming attacks.
|
||||
enable_queue: bool = True
|
||||
|
||||
# Protocol version we host with. Currently the default is 33 which
|
||||
|
|
@ -158,9 +187,9 @@ class ServerConfig:
|
|||
player_rejoin_cooldown: float = 10.0
|
||||
|
||||
|
||||
# NOTE: as much as possible, communication from the server-manager to the
|
||||
# child-process should go through these and not ad-hoc Python string commands
|
||||
# since this way is type safe.
|
||||
# NOTE: as much as possible, communication from the server-manager to
|
||||
# the child-process should go through these and not ad-hoc Python string
|
||||
# commands since this way is type safe.
|
||||
class ServerCommand:
|
||||
"""Base class for commands that can be sent to the server."""
|
||||
|
||||
|
|
|
|||
12
dist/ba_data/python/bacommon/transfer.py
vendored
12
dist/ba_data/python/bacommon/transfer.py
vendored
|
|
@ -31,7 +31,9 @@ class DirectoryManifest:
|
|||
|
||||
files: Annotated[dict[str, DirectoryManifestFile], IOAttrs('f')]
|
||||
|
||||
# _empty_hash: str | None = None
|
||||
# Soft-default added April 2024; can remove eventually once this
|
||||
# attr is widespread in client.
|
||||
exists: Annotated[bool, IOAttrs('e', soft_default=True)]
|
||||
|
||||
@classmethod
|
||||
def create_from_disk(cls, path: Path) -> DirectoryManifest:
|
||||
|
|
@ -42,6 +44,8 @@ class DirectoryManifest:
|
|||
pathstr = str(path)
|
||||
paths: list[str] = []
|
||||
|
||||
exists = path.exists()
|
||||
|
||||
if path.is_dir():
|
||||
# Build the full list of relative paths.
|
||||
for basename, _dirnames, filenames in os.walk(path):
|
||||
|
|
@ -51,7 +55,7 @@ class DirectoryManifest:
|
|||
# Make sure we end up with forward slashes no matter
|
||||
# what the os.* stuff above here was using.
|
||||
paths.append(Path(fullname[len(pathstr) + 1 :]).as_posix())
|
||||
elif path.exists():
|
||||
elif exists:
|
||||
# Just return a single file entry if path is not a dir.
|
||||
paths.append(path.as_posix())
|
||||
|
||||
|
|
@ -76,7 +80,9 @@ class DirectoryManifest:
|
|||
if cpus is None:
|
||||
cpus = 4
|
||||
with ThreadPoolExecutor(max_workers=cpus) as executor:
|
||||
return cls(files=dict(executor.map(_get_file_info, paths)))
|
||||
return cls(
|
||||
files=dict(executor.map(_get_file_info, paths)), exists=exists
|
||||
)
|
||||
|
||||
def validate(self) -> None:
|
||||
"""Log any odd data in the manifest; for debugging."""
|
||||
|
|
|
|||
8
dist/ba_data/python/baenv.py
vendored
8
dist/ba_data/python/baenv.py
vendored
|
|
@ -52,8 +52,8 @@ if TYPE_CHECKING:
|
|||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21766
|
||||
TARGET_BALLISTICA_VERSION = '1.7.33'
|
||||
TARGET_BALLISTICA_BUILD = 21879
|
||||
TARGET_BALLISTICA_VERSION = '1.7.35'
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -264,6 +264,10 @@ def _calc_data_dir(data_dir: str | None) -> str:
|
|||
def _setup_logging() -> LogHandler:
|
||||
from efro.log import setup_logging, LogLevel
|
||||
|
||||
# TODO: should set this up with individual loggers under a top level
|
||||
# 'ba' logger, and at that point we can kill off the
|
||||
# suppress_non_root_debug option since we'll only ever need to set
|
||||
# 'ba' to DEBUG at most.
|
||||
log_handler = setup_logging(
|
||||
log_path=None,
|
||||
level=LogLevel.DEBUG,
|
||||
|
|
|
|||
35
dist/ba_data/python/baplus/_cloud.py
vendored
35
dist/ba_data/python/baplus/_cloud.py
vendored
|
|
@ -57,8 +57,7 @@ class CloudSubsystem(babase.AppSubsystem):
|
|||
on_response: Callable[
|
||||
[bacommon.cloud.LoginProxyRequestResponse | Exception], None
|
||||
],
|
||||
) -> None:
|
||||
...
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def send_message_cb(
|
||||
|
|
@ -67,24 +66,21 @@ class CloudSubsystem(babase.AppSubsystem):
|
|||
on_response: Callable[
|
||||
[bacommon.cloud.LoginProxyStateQueryResponse | Exception], None
|
||||
],
|
||||
) -> None:
|
||||
...
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def send_message_cb(
|
||||
self,
|
||||
msg: bacommon.cloud.LoginProxyCompleteMessage,
|
||||
on_response: Callable[[None | Exception], None],
|
||||
) -> None:
|
||||
...
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def send_message_cb(
|
||||
self,
|
||||
msg: bacommon.cloud.PingMessage,
|
||||
on_response: Callable[[bacommon.cloud.PingResponse | Exception], None],
|
||||
) -> None:
|
||||
...
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def send_message_cb(
|
||||
|
|
@ -93,8 +89,7 @@ class CloudSubsystem(babase.AppSubsystem):
|
|||
on_response: Callable[
|
||||
[bacommon.cloud.SignInResponse | Exception], None
|
||||
],
|
||||
) -> None:
|
||||
...
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def send_message_cb(
|
||||
|
|
@ -103,8 +98,7 @@ class CloudSubsystem(babase.AppSubsystem):
|
|||
on_response: Callable[
|
||||
[bacommon.cloud.ManageAccountResponse | Exception], None
|
||||
],
|
||||
) -> None:
|
||||
...
|
||||
) -> None: ...
|
||||
|
||||
def send_message_cb(
|
||||
self,
|
||||
|
|
@ -129,20 +123,17 @@ class CloudSubsystem(babase.AppSubsystem):
|
|||
@overload
|
||||
def send_message(
|
||||
self, msg: bacommon.cloud.WorkspaceFetchMessage
|
||||
) -> bacommon.cloud.WorkspaceFetchResponse:
|
||||
...
|
||||
) -> bacommon.cloud.WorkspaceFetchResponse: ...
|
||||
|
||||
@overload
|
||||
def send_message(
|
||||
self, msg: bacommon.cloud.MerchAvailabilityMessage
|
||||
) -> bacommon.cloud.MerchAvailabilityResponse:
|
||||
...
|
||||
) -> bacommon.cloud.MerchAvailabilityResponse: ...
|
||||
|
||||
@overload
|
||||
def send_message(
|
||||
self, msg: bacommon.cloud.TestMessage
|
||||
) -> bacommon.cloud.TestResponse:
|
||||
...
|
||||
) -> bacommon.cloud.TestResponse: ...
|
||||
|
||||
def send_message(self, msg: Message) -> Response | None:
|
||||
"""Synchronously send a message to the cloud.
|
||||
|
|
@ -153,15 +144,13 @@ class CloudSubsystem(babase.AppSubsystem):
|
|||
|
||||
@overload
|
||||
async def send_message_async(
|
||||
self, msg: bacommon.cloud.PromoCodeMessage
|
||||
) -> bacommon.cloud.PromoCodeResponse:
|
||||
...
|
||||
self, msg: bacommon.cloud.SendInfoMessage
|
||||
) -> bacommon.cloud.SendInfoResponse: ...
|
||||
|
||||
@overload
|
||||
async def send_message_async(
|
||||
self, msg: bacommon.cloud.TestMessage
|
||||
) -> bacommon.cloud.TestResponse:
|
||||
...
|
||||
) -> bacommon.cloud.TestResponse: ...
|
||||
|
||||
async def send_message_async(self, msg: Message) -> Response | None:
|
||||
"""Synchronously send a message to the cloud.
|
||||
|
|
|
|||
3
dist/ba_data/python/baplus/_subsystem.py
vendored
3
dist/ba_data/python/baplus/_subsystem.py
vendored
|
|
@ -3,9 +3,8 @@
|
|||
"""Provides plus app subsystem."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
from babase import AppSubsystem
|
||||
|
||||
import _baplus
|
||||
|
|
|
|||
13
dist/ba_data/python/bascenev1/__init__.py
vendored
13
dist/ba_data/python/bascenev1/__init__.py
vendored
|
|
@ -120,6 +120,7 @@ from _bascenev1 import (
|
|||
release_keyboard_input,
|
||||
reset_random_player_names,
|
||||
resume_replay,
|
||||
seek_replay,
|
||||
broadcastmessage,
|
||||
SessionData,
|
||||
SessionPlayer,
|
||||
|
|
@ -133,6 +134,8 @@ from _bascenev1 import (
|
|||
set_public_party_enabled,
|
||||
set_public_party_max_size,
|
||||
set_public_party_name,
|
||||
set_public_party_public_address_ipv4,
|
||||
set_public_party_public_address_ipv6,
|
||||
set_public_party_queue_enabled,
|
||||
set_public_party_stats_url,
|
||||
set_replay_speed_exponent,
|
||||
|
|
@ -231,7 +234,11 @@ from bascenev1._settings import (
|
|||
IntSetting,
|
||||
Setting,
|
||||
)
|
||||
from bascenev1._session import Session, set_player_rejoin_cooldown
|
||||
from bascenev1._session import (
|
||||
Session,
|
||||
set_player_rejoin_cooldown,
|
||||
set_max_players_override,
|
||||
)
|
||||
from bascenev1._stats import PlayerScoredMessage, PlayerRecord, Stats
|
||||
from bascenev1._team import SessionTeam, Team, EmptyTeam
|
||||
from bascenev1._teamgame import TeamGameActivity
|
||||
|
|
@ -400,6 +407,7 @@ __all__ = [
|
|||
'release_keyboard_input',
|
||||
'reset_random_player_names',
|
||||
'resume_replay',
|
||||
'seek_replay',
|
||||
'safecolor',
|
||||
'screenmessage',
|
||||
'SceneV1AppMode',
|
||||
|
|
@ -423,9 +431,12 @@ __all__ = [
|
|||
'set_public_party_enabled',
|
||||
'set_public_party_max_size',
|
||||
'set_public_party_name',
|
||||
'set_public_party_public_address_ipv4',
|
||||
'set_public_party_public_address_ipv6',
|
||||
'set_public_party_queue_enabled',
|
||||
'set_public_party_stats_url',
|
||||
'set_player_rejoin_cooldown',
|
||||
'set_max_players_override',
|
||||
'set_replay_speed_exponent',
|
||||
'set_touchscreen_editing',
|
||||
'setmusic',
|
||||
|
|
|
|||
11
dist/ba_data/python/bascenev1/_activitytypes.py
vendored
11
dist/ba_data/python/bascenev1/_activitytypes.py
vendored
|
|
@ -3,9 +3,8 @@
|
|||
"""Some handy base class and special purpose Activity types."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
@ -203,9 +202,11 @@ class ScoreScreenActivity(Activity[EmptyPlayer, EmptyTeam]):
|
|||
sval = babase.Lstr(resource='pressAnyButtonText')
|
||||
|
||||
Text(
|
||||
self._custom_continue_message
|
||||
if self._custom_continue_message is not None
|
||||
else sval,
|
||||
(
|
||||
self._custom_continue_message
|
||||
if self._custom_continue_message is not None
|
||||
else sval
|
||||
),
|
||||
v_attach=Text.VAttach.BOTTOM,
|
||||
h_align=Text.HAlign.CENTER,
|
||||
flash=True,
|
||||
|
|
|
|||
10
dist/ba_data/python/bascenev1/_actor.py
vendored
10
dist/ba_data/python/bascenev1/_actor.py
vendored
|
|
@ -198,12 +198,14 @@ class Actor:
|
|||
# Overloads to convey our exact return type depending on 'doraise' value.
|
||||
|
||||
@overload
|
||||
def getactivity(self, doraise: Literal[True] = True) -> bascenev1.Activity:
|
||||
...
|
||||
def getactivity(
|
||||
self, doraise: Literal[True] = True
|
||||
) -> bascenev1.Activity: ...
|
||||
|
||||
@overload
|
||||
def getactivity(self, doraise: Literal[False]) -> bascenev1.Activity | None:
|
||||
...
|
||||
def getactivity(
|
||||
self, doraise: Literal[False]
|
||||
) -> bascenev1.Activity | None: ...
|
||||
|
||||
def getactivity(self, doraise: bool = True) -> bascenev1.Activity | None:
|
||||
"""Return the bascenev1.Activity this Actor is associated with.
|
||||
|
|
|
|||
3
dist/ba_data/python/bascenev1/_appmode.py
vendored
3
dist/ba_data/python/bascenev1/_appmode.py
vendored
|
|
@ -3,9 +3,8 @@
|
|||
"""Provides AppMode functionality."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
from bacommon.app import AppExperience
|
||||
from babase import (
|
||||
app,
|
||||
|
|
|
|||
3
dist/ba_data/python/bascenev1/_coopgame.py
vendored
3
dist/ba_data/python/bascenev1/_coopgame.py
vendored
|
|
@ -4,9 +4,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
from typing import TYPE_CHECKING, TypeVar, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
|
|||
13
dist/ba_data/python/bascenev1/_coopsession.py
vendored
13
dist/ba_data/python/bascenev1/_coopsession.py
vendored
|
|
@ -3,9 +3,8 @@
|
|||
"""Functionality related to coop-mode sessions."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
@ -61,6 +60,10 @@ class CoopSession(Session):
|
|||
max_players = classic.coop_session_args['max_players']
|
||||
else:
|
||||
max_players = app.config.get('Coop Game Max Players', 4)
|
||||
if 'submit_score' in classic.coop_session_args:
|
||||
submit_score = classic.coop_session_args['submit_score']
|
||||
else:
|
||||
submit_score = True
|
||||
|
||||
# print('FIXME: COOP SESSION WOULD CALC DEPS.')
|
||||
depsets: Sequence[bascenev1.DependencySet] = []
|
||||
|
|
@ -71,6 +74,7 @@ class CoopSession(Session):
|
|||
team_colors=TEAM_COLORS,
|
||||
min_players=min_players,
|
||||
max_players=max_players,
|
||||
submit_score=submit_score,
|
||||
)
|
||||
|
||||
# Tournament-ID if we correspond to a co-op tournament (otherwise None)
|
||||
|
|
@ -346,7 +350,10 @@ class CoopSession(Session):
|
|||
self.setactivity(next_game)
|
||||
|
||||
if not (env.demo or env.arcade):
|
||||
if self.tournament_id is not None:
|
||||
if (
|
||||
self.tournament_id is not None
|
||||
and classic.coop_session_args['submit_score']
|
||||
):
|
||||
self._custom_menu_ui = [
|
||||
{
|
||||
'label': babase.Lstr(resource='restartText'),
|
||||
|
|
|
|||
3
dist/ba_data/python/bascenev1/_dependency.py
vendored
3
dist/ba_data/python/bascenev1/_dependency.py
vendored
|
|
@ -5,9 +5,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import weakref
|
||||
from typing import Generic, TypeVar, TYPE_CHECKING
|
||||
from typing import Generic, TypeVar, TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
"""Functionality related to teams sessions."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ from __future__ import annotations
|
|||
|
||||
import random
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
from typing import TYPE_CHECKING, TypeVar, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ class GameResults:
|
|||
self._scores: dict[
|
||||
int, tuple[weakref.ref[bascenev1.SessionTeam], int | None]
|
||||
] = {}
|
||||
self._sessionteams: list[
|
||||
weakref.ref[bascenev1.SessionTeam]
|
||||
] | None = None
|
||||
self._sessionteams: list[weakref.ref[bascenev1.SessionTeam]] | None = (
|
||||
None
|
||||
)
|
||||
self._playerinfos: list[bascenev1.PlayerInfo] | None = None
|
||||
self._lower_is_better: bool | None = None
|
||||
self._score_label: str | None = None
|
||||
|
|
|
|||
11
dist/ba_data/python/bascenev1/_level.py
vendored
11
dist/ba_data/python/bascenev1/_level.py
vendored
|
|
@ -5,9 +5,8 @@ from __future__ import annotations
|
|||
|
||||
import copy
|
||||
import weakref
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -73,9 +72,11 @@ class Level:
|
|||
return babase.Lstr(
|
||||
translate=(
|
||||
'coopLevelNames',
|
||||
self._displayname
|
||||
if self._displayname is not None
|
||||
else self._name,
|
||||
(
|
||||
self._displayname
|
||||
if self._displayname is not None
|
||||
else self._name
|
||||
),
|
||||
),
|
||||
subs=[
|
||||
('${GAME}', self._gametype.get_display_string(self._settings))
|
||||
|
|
|
|||
10
dist/ba_data/python/bascenev1/_map.py
vendored
10
dist/ba_data/python/bascenev1/_map.py
vendored
|
|
@ -4,9 +4,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
@ -256,9 +255,7 @@ class Map(Actor):
|
|||
return (
|
||||
None
|
||||
if val is None
|
||||
else babase.vec3validate(val)
|
||||
if __debug__
|
||||
else val
|
||||
else babase.vec3validate(val) if __debug__ else val
|
||||
)
|
||||
|
||||
def get_def_points(self, name: str) -> list[Sequence[float]]:
|
||||
|
|
@ -334,8 +331,7 @@ class Map(Actor):
|
|||
closest_player_dist = 9999.0
|
||||
for ppt in player_pts:
|
||||
dist = (ppt - testpt).length()
|
||||
if dist < closest_player_dist:
|
||||
closest_player_dist = dist
|
||||
closest_player_dist = min(dist, closest_player_dist)
|
||||
if closest_player_dist > farthestpt_dist:
|
||||
farthestpt_dist = closest_player_dist
|
||||
farthestpt = testpt
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ from __future__ import annotations
|
|||
import copy
|
||||
import random
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
@ -67,8 +66,8 @@ class MultiTeamSession(Session):
|
|||
max_players=self.get_max_players(),
|
||||
)
|
||||
|
||||
self._series_length: int = classic.teams_series_length
|
||||
self._ffa_series_length: int = classic.ffa_series_length
|
||||
self._series_length: int = int(cfg.get('Teams Series Length', 7))
|
||||
self._ffa_series_length: int = int(cfg.get('FFA Series Length', 24))
|
||||
|
||||
show_tutorial = cfg.get('Show Tutorial', True)
|
||||
|
||||
|
|
|
|||
4
dist/ba_data/python/bascenev1/_nodeactor.py
vendored
4
dist/ba_data/python/bascenev1/_nodeactor.py
vendored
|
|
@ -4,9 +4,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from bascenev1._messages import DieMessage
|
||||
from bascenev1._actor import Actor
|
||||
|
|
|
|||
30
dist/ba_data/python/bascenev1/_playlist.py
vendored
30
dist/ba_data/python/bascenev1/_playlist.py
vendored
|
|
@ -89,18 +89,18 @@ def filter_playlist(
|
|||
'bs_king_of_the_hill.KingOfTheHillGame',
|
||||
'bastd.game.kingofthehill.KingOfTheHillGame',
|
||||
):
|
||||
entry[
|
||||
'type'
|
||||
] = 'bascenev1lib.game.kingofthehill.KingOfTheHillGame'
|
||||
entry['type'] = (
|
||||
'bascenev1lib.game.kingofthehill.KingOfTheHillGame'
|
||||
)
|
||||
if entry['type'] in (
|
||||
'Capture_the_Flag.CTFGame',
|
||||
'bsCaptureTheFlag.CTFGame',
|
||||
'bs_capture_the_flag.CTFGame',
|
||||
'bastd.game.capturetheflag.CaptureTheFlagGame',
|
||||
):
|
||||
entry[
|
||||
'type'
|
||||
] = 'bascenev1lib.game.capturetheflag.CaptureTheFlagGame'
|
||||
entry['type'] = (
|
||||
'bascenev1lib.game.capturetheflag.CaptureTheFlagGame'
|
||||
)
|
||||
if entry['type'] in (
|
||||
'Death_Match.DeathMatchGame',
|
||||
'bsDeathMatch.DeathMatchGame',
|
||||
|
|
@ -163,25 +163,25 @@ def filter_playlist(
|
|||
'bs_easter_egg_hunt.EasterEggHuntGame',
|
||||
'bastd.game.easteregghunt.EasterEggHuntGame',
|
||||
):
|
||||
entry[
|
||||
'type'
|
||||
] = 'bascenev1lib.game.easteregghunt.EasterEggHuntGame'
|
||||
entry['type'] = (
|
||||
'bascenev1lib.game.easteregghunt.EasterEggHuntGame'
|
||||
)
|
||||
if entry['type'] in (
|
||||
'bsMeteorShower.MeteorShowerGame',
|
||||
'bs_meteor_shower.MeteorShowerGame',
|
||||
'bastd.game.meteorshower.MeteorShowerGame',
|
||||
):
|
||||
entry[
|
||||
'type'
|
||||
] = 'bascenev1lib.game.meteorshower.MeteorShowerGame'
|
||||
entry['type'] = (
|
||||
'bascenev1lib.game.meteorshower.MeteorShowerGame'
|
||||
)
|
||||
if entry['type'] in (
|
||||
'bsTargetPractice.TargetPracticeGame',
|
||||
'bs_target_practice.TargetPracticeGame',
|
||||
'bastd.game.targetpractice.TargetPracticeGame',
|
||||
):
|
||||
entry[
|
||||
'type'
|
||||
] = 'bascenev1lib.game.targetpractice.TargetPracticeGame'
|
||||
entry['type'] = (
|
||||
'bascenev1lib.game.targetpractice.TargetPracticeGame'
|
||||
)
|
||||
|
||||
gameclass = babase.getclass(entry['type'], GameActivity)
|
||||
|
||||
|
|
|
|||
19
dist/ba_data/python/bascenev1/_session.py
vendored
19
dist/ba_data/python/bascenev1/_session.py
vendored
|
|
@ -23,6 +23,9 @@ if TYPE_CHECKING:
|
|||
# such as skipping respawn waits.
|
||||
_g_player_rejoin_cooldown: float = 0.0
|
||||
|
||||
# overrides the session's decision of max_players
|
||||
_max_players_override: int | None = None
|
||||
|
||||
|
||||
def set_player_rejoin_cooldown(cooldown: float) -> None:
|
||||
"""Set the cooldown for individual players rejoining after leaving."""
|
||||
|
|
@ -30,6 +33,12 @@ def set_player_rejoin_cooldown(cooldown: float) -> None:
|
|||
_g_player_rejoin_cooldown = max(0.0, cooldown)
|
||||
|
||||
|
||||
def set_max_players_override(max_players: int | None) -> None:
|
||||
"""Set the override for how many players can join a session"""
|
||||
global _max_players_override # pylint: disable=global-statement
|
||||
_max_players_override = max_players
|
||||
|
||||
|
||||
class Session:
|
||||
"""Defines a high level series of bascenev1.Activity-es.
|
||||
|
||||
|
|
@ -91,6 +100,7 @@ class Session:
|
|||
team_colors: Sequence[Sequence[float]] | None = None,
|
||||
min_players: int = 1,
|
||||
max_players: int = 8,
|
||||
submit_score: bool = True,
|
||||
):
|
||||
"""Instantiate a session.
|
||||
|
||||
|
|
@ -161,7 +171,12 @@ class Session:
|
|||
self.sessionteams = []
|
||||
self.sessionplayers = []
|
||||
self.min_players = min_players
|
||||
self.max_players = max_players
|
||||
self.max_players = (
|
||||
max_players
|
||||
if _max_players_override is None
|
||||
else _max_players_override
|
||||
)
|
||||
self.submit_score = submit_score
|
||||
|
||||
self.customdata = {}
|
||||
self._in_set_activity = False
|
||||
|
|
@ -255,7 +270,7 @@ class Session:
|
|||
babase.app.classic is not None
|
||||
and babase.app.classic.stress_test_update_timer is None
|
||||
):
|
||||
if len(self.sessionplayers) >= self.max_players:
|
||||
if len(self.sessionplayers) >= self.max_players >= 0:
|
||||
# Print a rejection message *only* to the client trying to
|
||||
# join (prevents spamming everyone else in the game).
|
||||
_bascenev1.getsound('error').play()
|
||||
|
|
|
|||
3
dist/ba_data/python/bascenev1/_teamgame.py
vendored
3
dist/ba_data/python/bascenev1/_teamgame.py
vendored
|
|
@ -5,9 +5,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
from typing import TYPE_CHECKING, TypeVar, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bascenev1
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import override
|
||||
|
||||
import bascenev1 as bs
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ from __future__ import annotations
|
|||
|
||||
import random
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
from bacommon.login import LoginType
|
||||
import bascenev1 as bs
|
||||
import bauiv1 as bui
|
||||
|
|
@ -125,6 +124,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
self._tournament_time_remaining: float | None = None
|
||||
self._tournament_time_remaining_text: Text | None = None
|
||||
self._tournament_time_remaining_text_timer: bs.BaseTimer | None = None
|
||||
self._submit_score = self.session.submit_score
|
||||
|
||||
# Stuff for activity skip by pressing button
|
||||
self._birth_time = bs.time()
|
||||
|
|
@ -395,11 +395,15 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
color=(0.45, 0.4, 0.5),
|
||||
position=(160, v_offs + 480),
|
||||
size=(350, 62),
|
||||
label=bui.Lstr(resource='tournamentStandingsText')
|
||||
if self.session.tournament_id is not None
|
||||
else bui.Lstr(resource='worldsBestScoresText')
|
||||
if self._score_type == 'points'
|
||||
else bui.Lstr(resource='worldsBestTimesText'),
|
||||
label=(
|
||||
bui.Lstr(resource='tournamentStandingsText')
|
||||
if self.session.tournament_id is not None
|
||||
else (
|
||||
bui.Lstr(resource='worldsBestScoresText')
|
||||
if self._score_type == 'points'
|
||||
else bui.Lstr(resource='worldsBestTimesText')
|
||||
)
|
||||
),
|
||||
autoselect=True,
|
||||
on_activate_call=bui.WeakCall(self._ui_worlds_best),
|
||||
transition_delay=delay + 1.9,
|
||||
|
|
@ -515,9 +519,11 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
|
||||
bui.containerwidget(
|
||||
edit=rootc,
|
||||
selected_child=next_button
|
||||
if (self._newly_complete and self._victory and show_next_button)
|
||||
else restart_button,
|
||||
selected_child=(
|
||||
next_button
|
||||
if (self._newly_complete and self._victory and show_next_button)
|
||||
else restart_button
|
||||
),
|
||||
on_cancel_call=menu_button.activate,
|
||||
)
|
||||
|
||||
|
|
@ -644,14 +650,16 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
and not (env.demo or env.arcade)
|
||||
):
|
||||
Text(
|
||||
bs.Lstr(
|
||||
value='${A}:\n',
|
||||
subs=[('${A}', bs.Lstr(resource='levelUnlockedText'))],
|
||||
)
|
||||
if self._newly_complete
|
||||
else bs.Lstr(
|
||||
value='${A}:\n',
|
||||
subs=[('${A}', bs.Lstr(resource='nextLevelText'))],
|
||||
(
|
||||
bs.Lstr(
|
||||
value='${A}:\n',
|
||||
subs=[('${A}', bs.Lstr(resource='levelUnlockedText'))],
|
||||
)
|
||||
if self._newly_complete
|
||||
else bs.Lstr(
|
||||
value='${A}:\n',
|
||||
subs=[('${A}', bs.Lstr(resource='nextLevelText'))],
|
||||
)
|
||||
),
|
||||
transition=Text.Transition.IN_RIGHT,
|
||||
transition_delay=5.2,
|
||||
|
|
@ -781,7 +789,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
transition_delay=2.0,
|
||||
)
|
||||
|
||||
if self._score is not None:
|
||||
if self._score is not None and self._submit_score:
|
||||
bs.timer(0.4, bs.WeakCall(self._play_drumroll))
|
||||
|
||||
# Add us to high scores, filter, and store.
|
||||
|
|
@ -860,11 +868,15 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
ts_h_offs = 210
|
||||
v_offs = 40
|
||||
txt = Text(
|
||||
bs.Lstr(resource='tournamentStandingsText')
|
||||
if self.session.tournament_id is not None
|
||||
else bs.Lstr(resource='worldsBestScoresText')
|
||||
if self._score_type == 'points'
|
||||
else bs.Lstr(resource='worldsBestTimesText'),
|
||||
(
|
||||
bs.Lstr(resource='tournamentStandingsText')
|
||||
if self.session.tournament_id is not None
|
||||
else (
|
||||
bs.Lstr(resource='worldsBestScoresText')
|
||||
if self._score_type == 'points'
|
||||
else bs.Lstr(resource='worldsBestTimesText')
|
||||
)
|
||||
),
|
||||
maxwidth=210,
|
||||
position=(ts_h_offs - 10, ts_height / 2 + 25 + v_offs + 20),
|
||||
transition=Text.Transition.IN_LEFT,
|
||||
|
|
@ -882,9 +894,11 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
ts_h_offs = -480
|
||||
v_offs = 40
|
||||
Text(
|
||||
bs.Lstr(resource='yourBestScoresText')
|
||||
if self._score_type == 'points'
|
||||
else bs.Lstr(resource='yourBestTimesText'),
|
||||
(
|
||||
bs.Lstr(resource='yourBestScoresText')
|
||||
if self._score_type == 'points'
|
||||
else bs.Lstr(resource='yourBestTimesText')
|
||||
),
|
||||
maxwidth=210,
|
||||
position=(ts_h_offs - 10, ts_height / 2 + 25 + v_offs + 20),
|
||||
transition=Text.Transition.IN_RIGHT,
|
||||
|
|
@ -948,9 +962,11 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
tdelay1 = times[i][0]
|
||||
tdelay2 = times[i][1]
|
||||
Text(
|
||||
str(display_scores[i][0])
|
||||
if self._score_type == 'points'
|
||||
else bs.timestring((display_scores[i][0] * 10) / 1000.0),
|
||||
(
|
||||
str(display_scores[i][0])
|
||||
if self._score_type == 'points'
|
||||
else bs.timestring((display_scores[i][0] * 10) / 1000.0)
|
||||
),
|
||||
position=(
|
||||
ts_h_offs + 20 + h_offs_extra,
|
||||
v_offs_extra
|
||||
|
|
@ -1127,9 +1143,11 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
tdelay2 = times[i][1]
|
||||
if name_str != '-':
|
||||
Text(
|
||||
str(score)
|
||||
if self._score_type == 'points'
|
||||
else bs.timestring((score * 10) / 1000.0),
|
||||
(
|
||||
str(score)
|
||||
if self._score_type == 'points'
|
||||
else bs.timestring((score * 10) / 1000.0)
|
||||
),
|
||||
position=(
|
||||
ts_h_offs + 20 + h_offs_extra,
|
||||
v_offs_extra
|
||||
|
|
@ -1313,9 +1331,11 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
|
||||
if name_str != '-':
|
||||
Text(
|
||||
str(score)
|
||||
if self._score_type == 'points'
|
||||
else bs.timestring((score * 10) / 1000.0),
|
||||
(
|
||||
str(score)
|
||||
if self._score_type == 'points'
|
||||
else bs.timestring((score * 10) / 1000.0)
|
||||
),
|
||||
position=(
|
||||
ts_h_offs + 20 + h_offs_extra,
|
||||
ts_height / 2
|
||||
|
|
@ -1376,7 +1396,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
assert self._show_info is not None
|
||||
available = self._show_info['results'] is not None
|
||||
|
||||
if available:
|
||||
if available and self._submit_score:
|
||||
error = (
|
||||
self._show_info['results']['error']
|
||||
if 'error' in self._show_info['results']
|
||||
|
|
@ -1509,7 +1529,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
maxwidth=400,
|
||||
transition_delay=1.0,
|
||||
).autoretain()
|
||||
else:
|
||||
elif self._submit_score:
|
||||
ZoomText(
|
||||
(
|
||||
('#' + str(player_rank))
|
||||
|
|
@ -1689,17 +1709,22 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
)
|
||||
if not self._newly_complete:
|
||||
Text(
|
||||
bs.Lstr(
|
||||
value='${A}${B}',
|
||||
subs=[
|
||||
('${A}', bs.Lstr(resource='newPersonalBestText')),
|
||||
('${B}', was_string),
|
||||
],
|
||||
)
|
||||
if new_best
|
||||
else bs.Lstr(
|
||||
resource='bestRatingText',
|
||||
subs=[('${RATING}', str(best_rank))],
|
||||
(
|
||||
bs.Lstr(
|
||||
value='${A}${B}',
|
||||
subs=[
|
||||
(
|
||||
'${A}',
|
||||
bs.Lstr(resource='newPersonalBestText'),
|
||||
),
|
||||
('${B}', was_string),
|
||||
],
|
||||
)
|
||||
if new_best
|
||||
else bs.Lstr(
|
||||
resource='bestRatingText',
|
||||
subs=[('${RATING}', str(best_rank))],
|
||||
)
|
||||
),
|
||||
position=(0, -165),
|
||||
color=(1, 1, 1, 0.7),
|
||||
|
|
@ -1727,9 +1752,10 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
transition_delay=0,
|
||||
).autoretain()
|
||||
|
||||
bs.timer(0.35, self._score_display_sound.play)
|
||||
if not error:
|
||||
bs.timer(0.35, self.cymbal_sound.play)
|
||||
if self._submit_score:
|
||||
bs.timer(0.35, self._score_display_sound.play)
|
||||
if not error:
|
||||
bs.timer(0.35, self.cymbal_sound.play)
|
||||
|
||||
def _show_fail(self) -> None:
|
||||
ZoomText(
|
||||
|
|
@ -1773,14 +1799,16 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
|||
jitter=1.0,
|
||||
).autoretain()
|
||||
Text(
|
||||
bs.Lstr(
|
||||
value='${A}:',
|
||||
subs=[('${A}', bs.Lstr(resource='finalScoreText'))],
|
||||
)
|
||||
if self._score_type == 'points'
|
||||
else bs.Lstr(
|
||||
value='${A}:',
|
||||
subs=[('${A}', bs.Lstr(resource='finalTimeText'))],
|
||||
(
|
||||
bs.Lstr(
|
||||
value='${A}:',
|
||||
subs=[('${A}', bs.Lstr(resource='finalScoreText'))],
|
||||
)
|
||||
if self._score_type == 'points'
|
||||
else bs.Lstr(
|
||||
value='${A}:',
|
||||
subs=[('${A}', bs.Lstr(resource='finalTimeText'))],
|
||||
)
|
||||
),
|
||||
maxwidth=300,
|
||||
position=(0, 200),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import override
|
||||
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import override
|
||||
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import override
|
||||
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.text import Text
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
"""Functionality related to teams mode score screen."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import override
|
||||
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.text import Text
|
||||
|
|
@ -199,9 +200,9 @@ class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity):
|
|||
ts_v_offset + (voffs + 15) * scale,
|
||||
),
|
||||
scale=scale,
|
||||
color=(1.0, 0.9, 0.5, 1.0)
|
||||
if highlight
|
||||
else (0.5, 0.5, 0.6, 0.5),
|
||||
color=(
|
||||
(1.0, 0.9, 0.5, 1.0) if highlight else (0.5, 0.5, 0.6, 0.5)
|
||||
),
|
||||
h_align=Text.HAlign.RIGHT,
|
||||
v_align=Text.VAlign.CENTER,
|
||||
maxwidth=maxwidth,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import override
|
||||
from typing import override
|
||||
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
|
||||
|
|
@ -374,9 +375,11 @@ class TeamSeriesVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
|||
tdelay -= 4 * t_incr
|
||||
v_offs -= 40
|
||||
Text(
|
||||
str(prec.team.customdata['score'])
|
||||
if self._is_ffa
|
||||
else str(prec.score),
|
||||
(
|
||||
str(prec.team.customdata['score'])
|
||||
if self._is_ffa
|
||||
else str(prec.score)
|
||||
),
|
||||
color=(0.5, 0.5, 0.5, 1.0),
|
||||
position=(ts_h_offs + 230, ts_height / 2 + v_offs),
|
||||
h_align=Text.HAlign.RIGHT,
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ from __future__ import annotations
|
|||
import random
|
||||
import weakref
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
from typing import TYPE_CHECKING, TypeVar, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.gameutils import SharedObjects
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -456,9 +455,11 @@ class ControlsGuide(bs.Actor):
|
|||
(
|
||||
'${B}',
|
||||
bs.Lstr(
|
||||
resource='holdAnyKeyText'
|
||||
if all_keyboards
|
||||
else 'holdAnyButtonText'
|
||||
resource=(
|
||||
'holdAnyKeyText'
|
||||
if all_keyboards
|
||||
else 'holdAnyButtonText'
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.gameutils import SharedObjects
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
"""Defines Actor(s)."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
import logging
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, TypeVar, overload
|
||||
from typing import TYPE_CHECKING, TypeVar, overload, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.spaz import Spaz
|
||||
|
|
@ -79,14 +78,12 @@ class PlayerSpaz(Spaz):
|
|||
@overload
|
||||
def getplayer(
|
||||
self, playertype: type[PlayerT], doraise: Literal[False] = False
|
||||
) -> PlayerT | None:
|
||||
...
|
||||
) -> PlayerT | None: ...
|
||||
|
||||
@overload
|
||||
def getplayer(
|
||||
self, playertype: type[PlayerT], doraise: Literal[True]
|
||||
) -> PlayerT:
|
||||
...
|
||||
) -> PlayerT: ...
|
||||
|
||||
def getplayer(
|
||||
self, playertype: type[PlayerT], doraise: bool = False
|
||||
|
|
@ -225,10 +222,20 @@ class PlayerSpaz(Spaz):
|
|||
elif isinstance(msg, bs.DieMessage):
|
||||
# Report player deaths to the game.
|
||||
if not self._dead:
|
||||
# Immediate-mode or left-game deaths don't count as 'kills'.
|
||||
killed = (
|
||||
not msg.immediate and msg.how is not bs.DeathType.LEFT_GAME
|
||||
# Was this player killed while being held?
|
||||
was_held = self.held_count > 0 and self.last_player_held_by
|
||||
# Was this player attacked before death?
|
||||
was_attacked_recently = (
|
||||
self.last_player_attacked_by
|
||||
and bs.time() - self.last_attacked_time < 4.0
|
||||
)
|
||||
# Leaving the game doesn't count as a kill *unless*
|
||||
# someone does it intentionally while being attacked.
|
||||
left_game_cleanly = msg.how is bs.DeathType.LEFT_GAME and not (
|
||||
was_held or was_attacked_recently
|
||||
)
|
||||
|
||||
killed = not (msg.immediate or left_game_cleanly)
|
||||
|
||||
activity = self._activity()
|
||||
|
||||
|
|
@ -238,7 +245,7 @@ class PlayerSpaz(Spaz):
|
|||
else:
|
||||
# If this player was being held at the time of death,
|
||||
# the holder is the killer.
|
||||
if self.held_count > 0 and self.last_player_held_by:
|
||||
if was_held:
|
||||
killerplayer = self.last_player_held_by
|
||||
else:
|
||||
# Otherwise, if they were attacked by someone in the
|
||||
|
|
@ -248,10 +255,7 @@ class PlayerSpaz(Spaz):
|
|||
# all bot kills would register as suicides; need to
|
||||
# change this from last_player_attacked_by to
|
||||
# something like last_actor_attacked_by to fix that.
|
||||
if (
|
||||
self.last_player_attacked_by
|
||||
and bs.time() - self.last_attacked_time < 4.0
|
||||
):
|
||||
if was_attacked_recently:
|
||||
killerplayer = self.last_player_attacked_by
|
||||
else:
|
||||
# ok, call it a suicide unless we're in co-op
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.gameutils import SharedObjects
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ class RespawnIcon:
|
|||
|
||||
def __init__(self, player: bs.Player, respawn_time: float):
|
||||
"""Instantiate with a Player and respawn_time (in seconds)."""
|
||||
# pylint: disable=too-many-locals
|
||||
self._visible = True
|
||||
self._dots_epic_only = False
|
||||
|
||||
on_right, offs_extra, respawn_icons = self._get_context(player)
|
||||
|
||||
|
|
@ -92,7 +94,7 @@ class RespawnIcon:
|
|||
assert self._name.node
|
||||
bs.animate(self._name.node, 'scale', {0: 0, 0.1: 0.5})
|
||||
|
||||
tpos = (-60 - h_offs if on_right else 60 + h_offs, -192 + offs)
|
||||
tpos = (-60 - h_offs if on_right else 60 + h_offs, -193 + offs)
|
||||
self._text: bs.NodeActor | None = bs.NodeActor(
|
||||
bs.newnode(
|
||||
'text',
|
||||
|
|
@ -109,11 +111,37 @@ class RespawnIcon:
|
|||
},
|
||||
)
|
||||
)
|
||||
dpos = [ipos[0] + (7 if on_right else -7), ipos[1] - 16]
|
||||
self._dec_text: bs.NodeActor | None = None
|
||||
if (
|
||||
self._dots_epic_only
|
||||
and bs.getactivity().globalsnode.slow_motion
|
||||
or not self._dots_epic_only
|
||||
):
|
||||
self._dec_text = bs.NodeActor(
|
||||
bs.newnode(
|
||||
'text',
|
||||
attrs={
|
||||
'position': dpos,
|
||||
'h_attach': 'right' if on_right else 'left',
|
||||
'h_align': 'right' if on_right else 'left',
|
||||
'scale': 0.65,
|
||||
'shadow': 0.5,
|
||||
'flatness': 0.5,
|
||||
'v_attach': 'top',
|
||||
'color': bs.safecolor(icon['tint_color']),
|
||||
'text': '',
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
assert self._text.node
|
||||
bs.animate(self._text.node, 'scale', {0: 0, 0.1: 0.9})
|
||||
if self._dec_text:
|
||||
bs.animate(self._dec_text.node, 'scale', {0: 0, 0.1: 0.65})
|
||||
|
||||
self._respawn_time = bs.time() + respawn_time
|
||||
self._dec_timer: bs.Timer | None = None
|
||||
self._update()
|
||||
self._timer: bs.Timer | None = bs.Timer(
|
||||
1.0, bs.WeakCall(self._update), repeat=True
|
||||
|
|
@ -128,7 +156,7 @@ class RespawnIcon:
|
|||
"""Return info on where we should be shown and stored."""
|
||||
activity = bs.getactivity()
|
||||
|
||||
if isinstance(bs.getsession(), bs.DualTeamSession):
|
||||
if isinstance(activity.session, bs.DualTeamSession):
|
||||
on_right = player.team.id % 2 == 1
|
||||
|
||||
# Store a list of icons in the team.
|
||||
|
|
@ -153,12 +181,43 @@ class RespawnIcon:
|
|||
offs_extra = -20
|
||||
return on_right, offs_extra, icons
|
||||
|
||||
def _dec_step(self, display: list) -> None:
|
||||
if not self._dec_text:
|
||||
self._dec_timer = None
|
||||
return
|
||||
old_text: bs.Lstr | str = self._dec_text.node.text
|
||||
iterate: int
|
||||
# Get the following display text using our current one.
|
||||
try:
|
||||
iterate = display.index(old_text) + 1
|
||||
# If we don't match any in the display list, we
|
||||
# can assume we've just started iterating.
|
||||
except ValueError:
|
||||
iterate = 0
|
||||
# Kill the timer if we're at the last iteration.
|
||||
if iterate >= len(display):
|
||||
self._dec_timer = None
|
||||
return
|
||||
self._dec_text.node.text = display[iterate]
|
||||
|
||||
def _update(self) -> None:
|
||||
remaining = int(round(self._respawn_time - bs.time()))
|
||||
|
||||
if remaining > 0:
|
||||
assert self._text is not None
|
||||
if self._text.node:
|
||||
self._text.node.text = str(remaining)
|
||||
if self._dec_text:
|
||||
# Display our decimal dots.
|
||||
self._dec_text.node.text = '...'
|
||||
# Start the timer to tick down.
|
||||
self._dec_timer = bs.Timer(
|
||||
0.25,
|
||||
bs.WeakCall(self._dec_step, ['..', '.', '']),
|
||||
repeat=True,
|
||||
)
|
||||
else:
|
||||
self._visible = False
|
||||
self._image = self._text = self._timer = self._name = None
|
||||
self._image = self._text = self._dec_text = self._timer = (
|
||||
self._name
|
||||
) = None
|
||||
|
|
|
|||
17
dist/ba_data/python/bascenev1lib/actor/spaz.py
vendored
17
dist/ba_data/python/bascenev1lib/actor/spaz.py
vendored
|
|
@ -7,13 +7,12 @@ from __future__ import annotations
|
|||
|
||||
import random
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
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.powerupbox import PowerupBoxFactory, PowerupBox
|
||||
from bascenev1lib.actor.spazfactory import SpazFactory
|
||||
from bascenev1lib.gameutils import SharedObjects
|
||||
|
||||
|
|
@ -68,6 +67,7 @@ class Spaz(bs.Actor):
|
|||
default_bomb_type = 'normal'
|
||||
default_boxing_gloves = False
|
||||
default_shields = False
|
||||
default_hitpoints = 1000
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -174,8 +174,8 @@ class Spaz(bs.Actor):
|
|||
setattr(node, attr, val)
|
||||
|
||||
bs.timer(1.0, bs.Call(_safesetattr, self.node, 'invincible', False))
|
||||
self.hitpoints = 1000
|
||||
self.hitpoints_max = 1000
|
||||
self.hitpoints = self.default_hitpoints
|
||||
self.hitpoints_max = self.default_hitpoints
|
||||
self.shield_hitpoints: int | None = None
|
||||
self.shield_hitpoints_max = 650
|
||||
self.shield_decay_rate = 0
|
||||
|
|
@ -629,7 +629,8 @@ class Spaz(bs.Actor):
|
|||
1000.0 * (tval + self.curse_time)
|
||||
)
|
||||
self._curse_timer = bs.Timer(
|
||||
5.0, bs.WeakCall(self.handlemessage, CurseExplodeMessage())
|
||||
self.curse_time,
|
||||
bs.WeakCall(self.handlemessage, CurseExplodeMessage()),
|
||||
)
|
||||
|
||||
def equip_boxing_gloves(self) -> None:
|
||||
|
|
@ -1227,6 +1228,10 @@ class Spaz(bs.Actor):
|
|||
return None
|
||||
node = bs.getcollision().opposingnode
|
||||
|
||||
# Don't want to physically affect powerups.
|
||||
if node.getdelegate(PowerupBox):
|
||||
return None
|
||||
|
||||
# Only allow one hit per node per punch.
|
||||
if node and (node not in self._punched_nodes):
|
||||
punch_momentum_angular = (
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ from __future__ import annotations
|
|||
import random
|
||||
import weakref
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
from bascenev1lib.actor.spaz import Spaz
|
||||
|
||||
|
|
@ -776,7 +775,6 @@ class ChargerBotPro(ChargerBot):
|
|||
|
||||
color = PRO_BOT_COLOR
|
||||
highlight = PRO_BOT_HIGHLIGHT
|
||||
default_shields = True
|
||||
default_boxing_gloves = True
|
||||
points_mult = 3
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -73,9 +72,11 @@ class TipsText(bs.Actor):
|
|||
next_tip = bs.Lstr(
|
||||
translate=(
|
||||
'tips',
|
||||
bs.app.classic.get_next_tip()
|
||||
if bs.app.classic is not None
|
||||
else '',
|
||||
(
|
||||
bs.app.classic.get_next_tip()
|
||||
if bs.app.classic is not None
|
||||
else ''
|
||||
),
|
||||
),
|
||||
subs=[('${REMOTE_APP_NAME}', get_remote_app_name())],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ from __future__ import annotations
|
|||
|
||||
import random
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
||||
|
|
@ -527,7 +526,38 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
|
|||
team.touch_return_timer = None
|
||||
team.touch_return_timer_ticking = None
|
||||
if team.flag_return_touches < 0:
|
||||
logging.exception('CTF flag_return_touches < 0')
|
||||
logging.error('CTF flag_return_touches < 0', stack_info=True)
|
||||
|
||||
def _handle_death_flag_capture(self, player: Player) -> None:
|
||||
"""Handles flag values when a player dies or leaves the game."""
|
||||
# Don't do anything if the player hasn't touched the flag at all.
|
||||
if not player.touching_own_flag:
|
||||
return
|
||||
|
||||
team = player.team
|
||||
|
||||
# For each "point" our player has touched theflag (Could be
|
||||
# multiple), deduct one from both our player and the flag's
|
||||
# return touches variable.
|
||||
for _ in range(player.touching_own_flag):
|
||||
# Deduct
|
||||
player.touching_own_flag -= 1
|
||||
|
||||
# (This was only incremented if we have non-zero
|
||||
# return-times).
|
||||
if float(self.flag_touch_return_time) > 0.0:
|
||||
team.flag_return_touches -= 1
|
||||
# Update our flag's timer accordingly
|
||||
# (Prevents immediate resets in case
|
||||
# there might be more people touching it).
|
||||
if team.flag_return_touches == 0:
|
||||
team.touch_return_timer = None
|
||||
team.touch_return_timer_ticking = None
|
||||
# Safety check, just to be sure!
|
||||
if team.flag_return_touches < 0:
|
||||
logging.error(
|
||||
'CTF flag_return_touches < 0', stack_info=True
|
||||
)
|
||||
|
||||
def _flash_base(self, team: Team, length: float = 2.0) -> None:
|
||||
light = bs.newnode(
|
||||
|
|
@ -591,6 +621,7 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
|
|||
def handlemessage(self, msg: Any) -> Any:
|
||||
if isinstance(msg, bs.PlayerDiedMessage):
|
||||
super().handlemessage(msg) # Augment standard behavior.
|
||||
self._handle_death_flag_capture(msg.getplayer(Player))
|
||||
self.respawn_player(msg.getplayer(Player))
|
||||
|
||||
elif isinstance(msg, FlagDiedMessage):
|
||||
|
|
@ -617,3 +648,8 @@ class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
|
|||
|
||||
else:
|
||||
super().handlemessage(msg)
|
||||
|
||||
@override
|
||||
def on_player_leave(self, player: Player) -> None:
|
||||
"""Prevents leaving players from capturing their flag."""
|
||||
self._handle_death_flag_capture(player)
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.flag import Flag
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.flag import Flag
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.bomb import Bomb
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.spazfactory import SpazFactory
|
||||
|
|
@ -478,7 +477,7 @@ class EliminationGame(bs.TeamGameActivity[Player, Team]):
|
|||
points.append(
|
||||
((start_pos - player_pos).length(), start_pos)
|
||||
)
|
||||
# Hmm.. we need to sorting vectors too?
|
||||
# Hmm.. we need to sort vectors too?
|
||||
points.sort(key=lambda x: x[0])
|
||||
return points[-1][1]
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ from __future__ import annotations
|
|||
import math
|
||||
import random
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.bomb import TNTSpawner
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ from __future__ import annotations
|
|||
|
||||
import weakref
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.flag import Flag
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.bomb import Bomb
|
||||
|
|
@ -73,6 +72,7 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
|
|||
self._last_player_death_time: float | None = None
|
||||
self._meteor_time = 2.0
|
||||
self._timer: OnScreenTimer | None = None
|
||||
self._ended: bool = False
|
||||
|
||||
# Some base class overrides:
|
||||
self.default_music = (
|
||||
|
|
@ -161,6 +161,10 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
|
|||
return None
|
||||
|
||||
def _check_end_game(self) -> None:
|
||||
# We don't want to end this activity more than once.
|
||||
if self._ended:
|
||||
return
|
||||
|
||||
living_team_count = 0
|
||||
for team in self.teams:
|
||||
for player in team.players:
|
||||
|
|
@ -270,4 +274,5 @@ class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
|
|||
# Submit the score value in milliseconds.
|
||||
results.set_team_score(team, int(1000.0 * longest_life))
|
||||
|
||||
self._ended = True
|
||||
self.end(results=results)
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.spazbot import (
|
||||
|
|
|
|||
386
dist/ba_data/python/bascenev1lib/game/onslaught.py
vendored
386
dist/ba_data/python/bascenev1lib/game/onslaught.py
vendored
|
|
@ -15,9 +15,8 @@ import random
|
|||
import logging
|
||||
from enum import Enum, unique
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.popuptext import PopupText
|
||||
|
|
@ -334,29 +333,37 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
Wave(
|
||||
base_angle=130,
|
||||
entries=[
|
||||
Spawn(BrawlerBotLite, spacing=5)
|
||||
if player_count > 1
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBotLite, spacing=5)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
Spawn(BrawlerBotLite, spacing=5),
|
||||
Spacing(30),
|
||||
Spawn(BomberBotLite, spacing=5)
|
||||
if player_count > 3
|
||||
else None,
|
||||
(
|
||||
Spawn(BomberBotLite, spacing=5)
|
||||
if player_count > 3
|
||||
else None
|
||||
),
|
||||
Spawn(BomberBotLite, spacing=5),
|
||||
Spacing(30),
|
||||
Spawn(BrawlerBotLite, spacing=5),
|
||||
Spawn(BrawlerBotLite, spacing=5)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBotLite, spacing=5)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
],
|
||||
),
|
||||
Wave(
|
||||
base_angle=195,
|
||||
entries=[
|
||||
Spawn(TriggerBot, spacing=90),
|
||||
Spawn(TriggerBot, spacing=90)
|
||||
if player_count > 1
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBot, spacing=90)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
@ -367,9 +374,11 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
self._waves = [
|
||||
Wave(
|
||||
entries=[
|
||||
Spawn(ChargerBot, Point.LEFT_UPPER_MORE)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(ChargerBot, Point.LEFT_UPPER_MORE)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spawn(ChargerBot, Point.LEFT_UPPER),
|
||||
]
|
||||
),
|
||||
|
|
@ -377,36 +386,50 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
entries=[
|
||||
Spawn(BomberBotStaticLite, Point.TURRET_TOP_RIGHT),
|
||||
Spawn(BrawlerBotLite, Point.RIGHT_UPPER),
|
||||
Spawn(BrawlerBotLite, Point.RIGHT_LOWER)
|
||||
if player_count > 1
|
||||
else None,
|
||||
Spawn(BomberBotStaticLite, Point.TURRET_BOTTOM_RIGHT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBotLite, Point.RIGHT_LOWER)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(
|
||||
BomberBotStaticLite, Point.TURRET_BOTTOM_RIGHT
|
||||
)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
Wave(
|
||||
entries=[
|
||||
Spawn(BomberBotStaticLite, Point.TURRET_BOTTOM_LEFT),
|
||||
Spawn(TriggerBot, Point.LEFT),
|
||||
Spawn(TriggerBot, Point.LEFT_LOWER)
|
||||
if player_count > 1
|
||||
else None,
|
||||
Spawn(TriggerBot, Point.LEFT_UPPER)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBot, Point.LEFT_LOWER)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(TriggerBot, Point.LEFT_UPPER)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
Wave(
|
||||
entries=[
|
||||
Spawn(BrawlerBotLite, Point.TOP_RIGHT),
|
||||
Spawn(BrawlerBot, Point.TOP_HALF_RIGHT)
|
||||
if player_count > 1
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBot, Point.TOP_HALF_RIGHT)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
Spawn(BrawlerBotLite, Point.TOP_LEFT),
|
||||
Spawn(BrawlerBotLite, Point.TOP_HALF_LEFT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBotLite, Point.TOP_HALF_LEFT)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spawn(BrawlerBot, Point.TOP),
|
||||
Spawn(BomberBotStaticLite, Point.TURRET_TOP_MIDDLE),
|
||||
]
|
||||
|
|
@ -416,12 +439,16 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spawn(TriggerBotStatic, Point.TURRET_BOTTOM_LEFT),
|
||||
Spawn(TriggerBotStatic, Point.TURRET_BOTTOM_RIGHT),
|
||||
Spawn(TriggerBot, Point.BOTTOM),
|
||||
Spawn(TriggerBot, Point.BOTTOM_HALF_RIGHT)
|
||||
if player_count > 1
|
||||
else None,
|
||||
Spawn(TriggerBot, Point.BOTTOM_HALF_LEFT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBot, Point.BOTTOM_HALF_RIGHT)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(TriggerBot, Point.BOTTOM_HALF_LEFT)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
Wave(
|
||||
|
|
@ -429,12 +456,16 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spawn(BomberBotStaticLite, Point.TURRET_TOP_LEFT),
|
||||
Spawn(BomberBotStaticLite, Point.TURRET_TOP_RIGHT),
|
||||
Spawn(ChargerBot, Point.BOTTOM),
|
||||
Spawn(ChargerBot, Point.BOTTOM_HALF_LEFT)
|
||||
if player_count > 1
|
||||
else None,
|
||||
Spawn(ChargerBot, Point.BOTTOM_HALF_RIGHT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(ChargerBot, Point.BOTTOM_HALF_LEFT)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(ChargerBot, Point.BOTTOM_HALF_RIGHT)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
|
@ -446,44 +477,62 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
Wave(
|
||||
base_angle=-50,
|
||||
entries=[
|
||||
Spawn(BrawlerBot, spacing=12)
|
||||
if player_count > 3
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBot, spacing=12)
|
||||
if player_count > 3
|
||||
else None
|
||||
),
|
||||
Spawn(BrawlerBot, spacing=12),
|
||||
Spawn(BomberBot, spacing=6),
|
||||
Spawn(BomberBot, spacing=6)
|
||||
if self._preset is Preset.PRO
|
||||
else None,
|
||||
Spawn(BomberBot, spacing=6)
|
||||
if player_count > 1
|
||||
else None,
|
||||
(
|
||||
Spawn(BomberBot, spacing=6)
|
||||
if self._preset is Preset.PRO
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(BomberBot, spacing=6)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
Spawn(BrawlerBot, spacing=12),
|
||||
Spawn(BrawlerBot, spacing=12)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBot, spacing=12)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
],
|
||||
),
|
||||
Wave(
|
||||
base_angle=180,
|
||||
entries=[
|
||||
Spawn(BrawlerBot, spacing=6)
|
||||
if player_count > 3
|
||||
else None,
|
||||
Spawn(BrawlerBot, spacing=6)
|
||||
if self._preset is Preset.PRO
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBot, spacing=6)
|
||||
if player_count > 3
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(BrawlerBot, spacing=6)
|
||||
if self._preset is Preset.PRO
|
||||
else None
|
||||
),
|
||||
Spawn(BrawlerBot, spacing=6),
|
||||
Spawn(ChargerBot, spacing=45),
|
||||
Spawn(ChargerBot, spacing=45)
|
||||
if player_count > 1
|
||||
else None,
|
||||
(
|
||||
Spawn(ChargerBot, spacing=45)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
Spawn(BrawlerBot, spacing=6),
|
||||
Spawn(BrawlerBot, spacing=6)
|
||||
if self._preset is Preset.PRO
|
||||
else None,
|
||||
Spawn(BrawlerBot, spacing=6)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBot, spacing=6)
|
||||
if self._preset is Preset.PRO
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(BrawlerBot, spacing=6)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
],
|
||||
),
|
||||
Wave(
|
||||
|
|
@ -492,15 +541,21 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spawn(ChargerBot, spacing=30),
|
||||
Spawn(TriggerBot, spacing=30),
|
||||
Spawn(TriggerBot, spacing=30),
|
||||
Spawn(TriggerBot, spacing=30)
|
||||
if self._preset is Preset.PRO
|
||||
else None,
|
||||
Spawn(TriggerBot, spacing=30)
|
||||
if player_count > 1
|
||||
else None,
|
||||
Spawn(TriggerBot, spacing=30)
|
||||
if player_count > 3
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBot, spacing=30)
|
||||
if self._preset is Preset.PRO
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(TriggerBot, spacing=30)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(TriggerBot, spacing=30)
|
||||
if player_count > 3
|
||||
else None
|
||||
),
|
||||
Spawn(ChargerBot, spacing=30),
|
||||
],
|
||||
),
|
||||
|
|
@ -508,16 +563,22 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
base_angle=90,
|
||||
entries=[
|
||||
Spawn(StickyBot, spacing=50),
|
||||
Spawn(StickyBot, spacing=50)
|
||||
if self._preset is Preset.PRO
|
||||
else None,
|
||||
(
|
||||
Spawn(StickyBot, spacing=50)
|
||||
if self._preset is Preset.PRO
|
||||
else None
|
||||
),
|
||||
Spawn(StickyBot, spacing=50),
|
||||
Spawn(StickyBot, spacing=50)
|
||||
if player_count > 1
|
||||
else None,
|
||||
Spawn(StickyBot, spacing=50)
|
||||
if player_count > 3
|
||||
else None,
|
||||
(
|
||||
Spawn(StickyBot, spacing=50)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(StickyBot, spacing=50)
|
||||
if player_count > 3
|
||||
else None
|
||||
),
|
||||
],
|
||||
),
|
||||
Wave(
|
||||
|
|
@ -525,14 +586,18 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
entries=[
|
||||
Spawn(TriggerBot, spacing=72),
|
||||
Spawn(TriggerBot, spacing=72),
|
||||
Spawn(TriggerBot, spacing=72)
|
||||
if self._preset is Preset.PRO
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBot, spacing=72)
|
||||
if self._preset is Preset.PRO
|
||||
else None
|
||||
),
|
||||
Spawn(TriggerBot, spacing=72),
|
||||
Spawn(TriggerBot, spacing=72),
|
||||
Spawn(TriggerBot, spacing=36)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBot, spacing=36)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
],
|
||||
),
|
||||
Wave(
|
||||
|
|
@ -540,15 +605,21 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
entries=[
|
||||
Spawn(ChargerBotProShielded, spacing=50),
|
||||
Spawn(ChargerBotProShielded, spacing=50),
|
||||
Spawn(ChargerBotProShielded, spacing=50)
|
||||
if self._preset is Preset.PRO
|
||||
else None,
|
||||
Spawn(ChargerBotProShielded, spacing=50)
|
||||
if player_count > 1
|
||||
else None,
|
||||
Spawn(ChargerBotProShielded, spacing=50)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(ChargerBotProShielded, spacing=50)
|
||||
if self._preset is Preset.PRO
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(ChargerBotProShielded, spacing=50)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(ChargerBotProShielded, spacing=50)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
@ -566,15 +637,21 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
self._waves = [
|
||||
Wave(
|
||||
entries=[
|
||||
Spawn(BomberBotProStatic, Point.TURRET_TOP_MIDDLE_LEFT)
|
||||
if hard
|
||||
else None,
|
||||
(
|
||||
Spawn(
|
||||
BomberBotProStatic, Point.TURRET_TOP_MIDDLE_LEFT
|
||||
)
|
||||
if hard
|
||||
else None
|
||||
),
|
||||
Spawn(
|
||||
BomberBotProStatic, Point.TURRET_TOP_MIDDLE_RIGHT
|
||||
),
|
||||
Spawn(BomberBotProStatic, Point.TURRET_TOP_LEFT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(BomberBotProStatic, Point.TURRET_TOP_LEFT)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spawn(ExplodeyBot, Point.TOP_RIGHT),
|
||||
Delay(4.0),
|
||||
Spawn(ExplodeyBot, Point.TOP_LEFT),
|
||||
|
|
@ -584,9 +661,11 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
entries=[
|
||||
Spawn(ChargerBot, Point.LEFT),
|
||||
Spawn(ChargerBot, Point.RIGHT),
|
||||
Spawn(ChargerBot, Point.RIGHT_UPPER_MORE)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(ChargerBot, Point.RIGHT_UPPER_MORE)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spawn(BomberBotProStatic, Point.TURRET_TOP_LEFT),
|
||||
Spawn(BomberBotProStatic, Point.TURRET_TOP_RIGHT),
|
||||
]
|
||||
|
|
@ -594,29 +673,39 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
Wave(
|
||||
entries=[
|
||||
Spawn(TriggerBotPro, Point.TOP_RIGHT),
|
||||
Spawn(TriggerBotPro, Point.RIGHT_UPPER_MORE)
|
||||
if player_count > 1
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBotPro, Point.RIGHT_UPPER_MORE)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
Spawn(TriggerBotPro, Point.RIGHT_UPPER),
|
||||
Spawn(TriggerBotPro, Point.RIGHT_LOWER)
|
||||
if hard
|
||||
else None,
|
||||
Spawn(TriggerBotPro, Point.RIGHT_LOWER_MORE)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBotPro, Point.RIGHT_LOWER)
|
||||
if hard
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(TriggerBotPro, Point.RIGHT_LOWER_MORE)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spawn(TriggerBotPro, Point.BOTTOM_RIGHT),
|
||||
]
|
||||
),
|
||||
Wave(
|
||||
entries=[
|
||||
Spawn(ChargerBotProShielded, Point.BOTTOM_RIGHT),
|
||||
Spawn(ChargerBotProShielded, Point.BOTTOM)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(ChargerBotProShielded, Point.BOTTOM)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spawn(ChargerBotProShielded, Point.BOTTOM_LEFT),
|
||||
Spawn(ChargerBotProShielded, Point.TOP)
|
||||
if hard
|
||||
else None,
|
||||
(
|
||||
Spawn(ChargerBotProShielded, Point.TOP)
|
||||
if hard
|
||||
else None
|
||||
),
|
||||
Spawn(BomberBotProStatic, Point.TURRET_TOP_MIDDLE),
|
||||
]
|
||||
),
|
||||
|
|
@ -643,12 +732,21 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spawn(BomberBotProStatic, Point.TURRET_TOP_RIGHT),
|
||||
Spawn(BomberBotProStatic, Point.TURRET_BOTTOM_LEFT),
|
||||
Spawn(BomberBotProStatic, Point.TURRET_BOTTOM_RIGHT),
|
||||
Spawn(BomberBotProStatic, Point.TURRET_TOP_MIDDLE_LEFT)
|
||||
if hard
|
||||
else None,
|
||||
Spawn(BomberBotProStatic, Point.TURRET_TOP_MIDDLE_RIGHT)
|
||||
if hard
|
||||
else None,
|
||||
(
|
||||
Spawn(
|
||||
BomberBotProStatic, Point.TURRET_TOP_MIDDLE_LEFT
|
||||
)
|
||||
if hard
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(
|
||||
BomberBotProStatic,
|
||||
Point.TURRET_TOP_MIDDLE_RIGHT,
|
||||
)
|
||||
if hard
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
|
@ -667,12 +765,14 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
|
|||
# Spit out a few powerups and start dropping more shortly.
|
||||
self._drop_powerups(
|
||||
standard_points=True,
|
||||
poweruptype='curse'
|
||||
if self._preset in [Preset.UBER, Preset.UBER_EASY]
|
||||
else (
|
||||
'land_mines'
|
||||
if self._preset in [Preset.ROOKIE, Preset.ROOKIE_EASY]
|
||||
else None
|
||||
poweruptype=(
|
||||
'curse'
|
||||
if self._preset in [Preset.UBER, Preset.UBER_EASY]
|
||||
else (
|
||||
'land_mines'
|
||||
if self._preset in [Preset.ROOKIE, Preset.ROOKIE_EASY]
|
||||
else None
|
||||
)
|
||||
),
|
||||
)
|
||||
bs.timer(4.0, self._start_powerup_drops)
|
||||
|
|
|
|||
11
dist/ba_data/python/bascenev1lib/game/race.py
vendored
11
dist/ba_data/python/bascenev1lib/game/race.py
vendored
|
|
@ -9,10 +9,9 @@ from __future__ import annotations
|
|||
|
||||
import random
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
from dataclasses import dataclass
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.bomb import Bomb
|
||||
|
|
@ -778,9 +777,11 @@ class RaceGame(bs.TeamGameActivity[Player, Team]):
|
|||
assert self._timer is not None
|
||||
if self._timer.has_started():
|
||||
self._timer.stop(
|
||||
endtime=None
|
||||
if self._last_team_time is None
|
||||
else (self._timer.getstarttime() + self._last_team_time)
|
||||
endtime=(
|
||||
None
|
||||
if self._last_team_time is None
|
||||
else (self._timer.getstarttime() + self._last_team_time)
|
||||
)
|
||||
)
|
||||
|
||||
results = bs.GameResults()
|
||||
|
|
|
|||
162
dist/ba_data/python/bascenev1lib/game/runaround.py
vendored
162
dist/ba_data/python/bascenev1lib/game/runaround.py
vendored
|
|
@ -14,9 +14,8 @@ import random
|
|||
import logging
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, cast, Sequence, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.popuptext import PopupText
|
||||
|
|
@ -45,7 +44,7 @@ from bascenev1lib.actor.spazbot import (
|
|||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Sequence
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Preset(Enum):
|
||||
|
|
@ -190,6 +189,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
self._lives_text: bs.NodeActor | None = None
|
||||
self._flawless = True
|
||||
self._time_bonus_timer: bs.Timer | None = None
|
||||
self._lives_hbtime: bs.Timer | None = None
|
||||
self._time_bonus_text: bs.NodeActor | None = None
|
||||
self._time_bonus_mult: float | None = None
|
||||
self._wave_text: bs.NodeActor | None = None
|
||||
|
|
@ -279,9 +279,11 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spacing(duration=1.0),
|
||||
Spawn(TriggerBot, path=3),
|
||||
Spacing(duration=1.0),
|
||||
Spawn(TriggerBot, path=1)
|
||||
if (player_count > 1 and hard)
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBot, path=1)
|
||||
if (player_count > 1 and hard)
|
||||
else None
|
||||
),
|
||||
Spacing(duration=1.0),
|
||||
Spawn(TriggerBot, path=2) if player_count > 2 else None,
|
||||
Spacing(duration=1.0),
|
||||
|
|
@ -320,17 +322,23 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spacing(duration=1.5),
|
||||
Spawn(BomberBotProShielded, path=1) if hard else None,
|
||||
Spacing(duration=1.5) if hard else None,
|
||||
Spawn(BomberBotProShielded, path=3)
|
||||
if player_count > 1
|
||||
else None,
|
||||
(
|
||||
Spawn(BomberBotProShielded, path=3)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
Spacing(duration=1.5),
|
||||
Spawn(BomberBotProShielded, path=2)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(BomberBotProShielded, path=2)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spacing(duration=1.5),
|
||||
Spawn(BomberBotProShielded, path=1)
|
||||
if player_count > 3
|
||||
else None,
|
||||
(
|
||||
Spawn(BomberBotProShielded, path=1)
|
||||
if player_count > 3
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
|
@ -352,9 +360,11 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
BrawlerBotPro if hard else BrawlerBot,
|
||||
point=Point.BOTTOM_LEFT,
|
||||
),
|
||||
Spawn(BrawlerBotPro, point=Point.BOTTOM_RIGHT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(BrawlerBotPro, point=Point.BOTTOM_RIGHT)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
Wave(
|
||||
|
|
@ -375,9 +385,11 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spawn(BomberBotProShielded, path=3),
|
||||
Spawn(BomberBotProShielded, path=3),
|
||||
Spawn(ChargerBot, point=Point.BOTTOM_RIGHT),
|
||||
Spawn(ChargerBot, point=Point.BOTTOM_LEFT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(ChargerBot, point=Point.BOTTOM_LEFT)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
Wave(
|
||||
|
|
@ -388,12 +400,16 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spawn(TriggerBotPro, path=1 if hard else 2),
|
||||
Spawn(TriggerBotPro, path=1 if hard else 2),
|
||||
Spawn(TriggerBotPro, path=1 if hard else 2),
|
||||
Spawn(TriggerBotPro, path=1 if hard else 2)
|
||||
if player_count > 1
|
||||
else None,
|
||||
Spawn(TriggerBotPro, path=1 if hard else 2)
|
||||
if player_count > 3
|
||||
else None,
|
||||
(
|
||||
Spawn(TriggerBotPro, path=1 if hard else 2)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(TriggerBotPro, path=1 if hard else 2)
|
||||
if player_count > 3
|
||||
else None
|
||||
),
|
||||
]
|
||||
),
|
||||
Wave(
|
||||
|
|
@ -402,12 +418,20 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
TriggerBotProShielded if hard else TriggerBotPro,
|
||||
point=Point.BOTTOM_LEFT,
|
||||
),
|
||||
Spawn(TriggerBotProShielded, point=Point.BOTTOM_RIGHT)
|
||||
if hard
|
||||
else None,
|
||||
Spawn(TriggerBotProShielded, point=Point.BOTTOM_RIGHT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(
|
||||
TriggerBotProShielded, point=Point.BOTTOM_RIGHT
|
||||
)
|
||||
if hard
|
||||
else None
|
||||
),
|
||||
(
|
||||
Spawn(
|
||||
TriggerBotProShielded, point=Point.BOTTOM_RIGHT
|
||||
)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spawn(BomberBot, path=3),
|
||||
Spawn(BomberBot, path=3),
|
||||
Spacing(duration=5.0),
|
||||
|
|
@ -425,15 +449,19 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
Spawn(StickyBot, point=Point.BOTTOM_RIGHT),
|
||||
Spawn(BomberBotProShielded, path=2),
|
||||
Spawn(BomberBotProShielded, path=2),
|
||||
Spawn(StickyBot, point=Point.BOTTOM_RIGHT)
|
||||
if player_count > 2
|
||||
else None,
|
||||
(
|
||||
Spawn(StickyBot, point=Point.BOTTOM_RIGHT)
|
||||
if player_count > 2
|
||||
else None
|
||||
),
|
||||
Spawn(BomberBotProShielded, path=2),
|
||||
Spawn(ExplodeyBot, point=Point.BOTTOM_LEFT),
|
||||
Spawn(BomberBotProShielded, path=2),
|
||||
Spawn(BomberBotProShielded, path=2)
|
||||
if player_count > 1
|
||||
else None,
|
||||
(
|
||||
Spawn(BomberBotProShielded, path=2)
|
||||
if player_count > 1
|
||||
else None
|
||||
),
|
||||
Spacing(duration=5.0),
|
||||
Spawn(StickyBot, point=Point.BOTTOM_LEFT),
|
||||
Spacing(duration=2.0),
|
||||
|
|
@ -461,9 +489,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
l_offs = (
|
||||
-80
|
||||
if uiscale is bs.UIScale.SMALL
|
||||
else -40
|
||||
if uiscale is bs.UIScale.MEDIUM
|
||||
else 0
|
||||
else -40 if uiscale is bs.UIScale.MEDIUM else 0
|
||||
)
|
||||
|
||||
self._lives_bg = bs.NodeActor(
|
||||
|
|
@ -525,6 +551,18 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
if self._lives == 0:
|
||||
self._bots.stop_moving()
|
||||
self.continue_or_end_game()
|
||||
|
||||
# Heartbeat behavior
|
||||
if self._lives < 5:
|
||||
hbtime = 0.39 + (0.21 * self._lives)
|
||||
self._lives_hbtime = bs.Timer(
|
||||
hbtime, lambda: self.heart_dyin(True, hbtime), repeat=True
|
||||
)
|
||||
self.heart_dyin(True)
|
||||
else:
|
||||
self._lives_hbtime = None
|
||||
self.heart_dyin(False)
|
||||
|
||||
assert self._lives_text is not None
|
||||
assert self._lives_text.node
|
||||
self._lives_text.node.text = str(self._lives)
|
||||
|
|
@ -1366,3 +1404,43 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
|
|||
|
||||
def _set_can_end_wave(self) -> None:
|
||||
self._can_end_wave = True
|
||||
|
||||
def heart_dyin(self, status: bool, time: float = 1.22) -> None:
|
||||
"""Makes the UI heart beat at low health."""
|
||||
assert self._lives_bg is not None
|
||||
if self._lives_bg.node.exists():
|
||||
return
|
||||
heart = self._lives_bg.node
|
||||
|
||||
# Make the heart beat intensely!
|
||||
if status:
|
||||
bs.animate_array(
|
||||
heart,
|
||||
'scale',
|
||||
2,
|
||||
{
|
||||
0: (90, 90),
|
||||
time * 0.1: (105, 105),
|
||||
time * 0.21: (88, 88),
|
||||
time * 0.42: (90, 90),
|
||||
time * 0.52: (105, 105),
|
||||
time * 0.63: (88, 88),
|
||||
time: (90, 90),
|
||||
},
|
||||
)
|
||||
|
||||
# Neutralize heartbeat (Done did when dead.)
|
||||
else:
|
||||
# Ew; janky old scenev1 has a single 'Node' Python type so
|
||||
# it thinks heart.scale could be a few different things
|
||||
# (float, Sequence[float], etc.). So we have to force the
|
||||
# issue with a cast(). This should go away with scenev2/etc.
|
||||
bs.animate_array(
|
||||
heart,
|
||||
'scale',
|
||||
2,
|
||||
{
|
||||
0.0: cast(Sequence[float], heart.scale),
|
||||
time: (90, 90),
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
||||
|
|
@ -321,11 +320,15 @@ class Target(bs.Actor):
|
|||
bs.getsound(
|
||||
'orchestraHit4'
|
||||
if streak > 3
|
||||
else 'orchestraHit3'
|
||||
if streak > 2
|
||||
else 'orchestraHit2'
|
||||
if streak > 1
|
||||
else 'orchestraHit'
|
||||
else (
|
||||
'orchestraHit3'
|
||||
if streak > 2
|
||||
else (
|
||||
'orchestraHit2'
|
||||
if streak > 1
|
||||
else 'orchestraHit'
|
||||
)
|
||||
)
|
||||
).play()
|
||||
elif dist <= self._r2 + self._rfudge:
|
||||
self._nodes[0].color = cdull
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ from __future__ import annotations
|
|||
import random
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
||||
|
|
|
|||
15
dist/ba_data/python/bascenev1lib/mainmenu.py
vendored
15
dist/ba_data/python/bascenev1lib/mainmenu.py
vendored
|
|
@ -8,9 +8,8 @@ from __future__ import annotations
|
|||
import time
|
||||
import random
|
||||
import weakref
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
import bauiv1 as bui
|
||||
|
||||
|
|
@ -134,8 +133,8 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
|||
text = bs.Lstr(
|
||||
value='${V} (${B}) (${D})',
|
||||
subs=[
|
||||
('${V}', app.env.version),
|
||||
('${B}', str(app.env.build_number)),
|
||||
('${V}', app.env.engine_version),
|
||||
('${B}', str(app.env.engine_build_number)),
|
||||
('${D}', bs.Lstr(resource='debugText')),
|
||||
],
|
||||
)
|
||||
|
|
@ -143,12 +142,14 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
|||
text = bs.Lstr(
|
||||
value='${V} (${B})',
|
||||
subs=[
|
||||
('${V}', app.env.version),
|
||||
('${B}', str(app.env.build_number)),
|
||||
('${V}', app.env.engine_version),
|
||||
('${B}', str(app.env.engine_build_number)),
|
||||
],
|
||||
)
|
||||
else:
|
||||
text = bs.Lstr(value='${V}', subs=[('${V}', app.env.version)])
|
||||
text = bs.Lstr(
|
||||
value='${V}', subs=[('${V}', app.env.engine_version)]
|
||||
)
|
||||
scale = 0.9 if (uiscale is bs.UIScale.SMALL or vr_mode) else 0.7
|
||||
color = (1, 1, 1, 1) if vr_mode else (0.5, 0.6, 0.5, 0.7)
|
||||
self.version = bs.NodeActor(
|
||||
|
|
|
|||
3
dist/ba_data/python/bascenev1lib/maps.py
vendored
3
dist/ba_data/python/bascenev1lib/maps.py
vendored
|
|
@ -5,9 +5,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.gameutils import SharedObjects
|
||||
|
|
|
|||
3
dist/ba_data/python/bascenev1lib/tutorial.py
vendored
3
dist/ba_data/python/bascenev1lib/tutorial.py
vendored
|
|
@ -17,9 +17,8 @@ from __future__ import annotations
|
|||
import math
|
||||
import logging
|
||||
from collections import deque
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import bascenev1 as bs
|
||||
|
||||
from bascenev1lib.actor.spaz import Spaz
|
||||
|
|
|
|||
10
dist/ba_data/python/bauiv1/__init__.py
vendored
10
dist/ba_data/python/bauiv1/__init__.py
vendored
|
|
@ -70,6 +70,11 @@ from babase import (
|
|||
native_review_request_supported,
|
||||
NotFoundError,
|
||||
open_file_externally,
|
||||
open_url,
|
||||
overlay_web_browser_close,
|
||||
overlay_web_browser_is_open,
|
||||
overlay_web_browser_is_supported,
|
||||
overlay_web_browser_open_url,
|
||||
Permission,
|
||||
Plugin,
|
||||
PluginSpec,
|
||||
|
|
@ -106,7 +111,6 @@ from _bauiv1 import (
|
|||
imagewidget,
|
||||
is_party_icon_visible,
|
||||
Mesh,
|
||||
open_url,
|
||||
rowwidget,
|
||||
scrollwidget,
|
||||
set_party_icon_always_visible,
|
||||
|
|
@ -191,6 +195,10 @@ __all__ = [
|
|||
'NotFoundError',
|
||||
'open_file_externally',
|
||||
'open_url',
|
||||
'overlay_web_browser_close',
|
||||
'overlay_web_browser_is_open',
|
||||
'overlay_web_browser_is_supported',
|
||||
'overlay_web_browser_open_url',
|
||||
'Permission',
|
||||
'Plugin',
|
||||
'PluginSpec',
|
||||
|
|
|
|||
8
dist/ba_data/python/bauiv1/_subsystem.py
vendored
8
dist/ba_data/python/bauiv1/_subsystem.py
vendored
|
|
@ -6,9 +6,8 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bauiv1
|
||||
|
|
@ -43,7 +42,10 @@ class UIV1Subsystem(babase.AppSubsystem):
|
|||
|
||||
self._uiscale: babase.UIScale
|
||||
|
||||
interfacetype = env['ui_scale']
|
||||
interfacetype = babase.app.config.get('UI Scale', env['ui_scale'])
|
||||
if interfacetype == 'auto':
|
||||
interfacetype = env['ui_scale']
|
||||
|
||||
if interfacetype == 'large':
|
||||
self._uiscale = babase.UIScale.LARGE
|
||||
elif interfacetype == 'medium':
|
||||
|
|
|
|||
7
dist/ba_data/python/bauiv1/_uitypes.py
vendored
7
dist/ba_data/python/bauiv1/_uitypes.py
vendored
|
|
@ -7,9 +7,8 @@ from __future__ import annotations
|
|||
import os
|
||||
import weakref
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, override
|
||||
|
||||
from typing_extensions import override
|
||||
import babase
|
||||
|
||||
import _bauiv1
|
||||
|
|
@ -164,9 +163,7 @@ class UIController:
|
|||
entrynew = (
|
||||
self._dialog_stack[-1]
|
||||
if self._dialog_stack
|
||||
else self._main_stack[-1]
|
||||
if self._main_stack
|
||||
else None
|
||||
else self._main_stack[-1] if self._main_stack else None
|
||||
)
|
||||
if entrynew is not None:
|
||||
entrynew.create()
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue