From 5b4282eb11d02b47e20735c50bdddca8cd0dba5a Mon Sep 17 00:00:00 2001
From: Eric Froemling
Date: Tue, 2 Jun 2020 18:46:38 -0700
Subject: [PATCH] Made game settings type safe
---
.efrocachemap | 24 +-
.idea/dictionaries/ericf.xml | 4 +
assets/.asset_manifest_public.json | 2 +
assets/Makefile | 7 +
assets/src/ba_data/python/_ba.py | 2 +-
assets/src/ba_data/python/ba/__init__.py | 5 +-
assets/src/ba_data/python/ba/_appdelegate.py | 5 +-
assets/src/ba_data/python/ba/_coopgame.py | 2 +-
assets/src/ba_data/python/ba/_coopsession.py | 16 +-
assets/src/ba_data/python/ba/_gameactivity.py | 36 +--
assets/src/ba_data/python/ba/_gameresults.py | 10 +-
assets/src/ba_data/python/ba/_gamesettings.py | 102 ++++++++
assets/src/ba_data/python/ba/_level.py | 2 +-
assets/src/ba_data/python/ba/_playlist.py | 9 +-
assets/src/ba_data/python/ba/_score.py | 4 +-
.../src/ba_data/python/bastd/appdelegate.py | 5 +-
.../src/ba_data/python/bastd/game/assault.py | 49 ++--
.../python/bastd/game/capturetheflag.py | 67 ++---
.../ba_data/python/bastd/game/chosenone.py | 61 +++--
.../src/ba_data/python/bastd/game/conquest.py | 40 +--
.../ba_data/python/bastd/game/deathmatch.py | 64 +++--
.../python/bastd/game/easteregghunt.py | 4 +-
.../ba_data/python/bastd/game/elimination.py | 69 ++---
.../src/ba_data/python/bastd/game/football.py | 52 ++--
.../src/ba_data/python/bastd/game/hockey.py | 47 ++--
.../src/ba_data/python/bastd/game/keepaway.py | 49 ++--
.../python/bastd/game/kingofthehill.py | 49 ++--
.../ba_data/python/bastd/game/meteorshower.py | 8 +-
.../ba_data/python/bastd/game/ninjafight.py | 6 +-
assets/src/ba_data/python/bastd/game/race.py | 76 +++---
.../python/bastd/game/targetpractice.py | 15 +-
.../python/bastd/ui/playlist/editgame.py | 60 ++---
docs/ba_module.md | 236 +++++++++++++-----
33 files changed, 764 insertions(+), 423 deletions(-)
create mode 100644 assets/src/ba_data/python/ba/_gamesettings.py
diff --git a/.efrocachemap b/.efrocachemap
index 9959a6e0..988b9239 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -4132,16 +4132,16 @@
"assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c",
"assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb",
"assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe",
- "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2e/aa/9bcdf166975aa5295669df48f641",
- "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d8/44/091ec27299abfb3e4a12efbb4016",
- "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/1b/2f/99bcfa67fb5a77d9d80883fcc9a4",
- "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f6/f5/fbd4514b7ca4663f917ee848acc5",
- "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8b/3c/7f4895731a58ab99c8deb2caa563",
- "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ba/d5/5a51c250695ce84b0cf2d59ef447",
- "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/44/e9/33f406d3ff1a7a5ec5c0d7f185e6",
- "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/53/55/1f3e23b7c6e975056854005de9aa",
- "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/c5/5d/56272619667b1fcd87b759d8688d",
- "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/8b/d0/24e71e25ac40fbd513e21c318ba4",
- "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/8a/6a/ba53af1a84abfa764213f1791ea2",
- "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/d8/7b/b32727ec4d7e899d54ebe83e1203"
+ "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1f/1d/c3e431b8c9116fb76eb10c7c3351",
+ "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/66/b8/9cb66184745ef1d783af3f7d3f22",
+ "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/52/e8/4826278e2030d629a682a8de070f",
+ "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a5/8a/9e07236256d006f0357d5d1aa887",
+ "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/81/38/985294f68973344e2f012fe93c45",
+ "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d3/57/2cdf16147cdd25263ba76aa40198",
+ "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a7/ec/1d64c0c31d58ef530b6894fabf2a",
+ "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4c/ea/c136ba4360d210842915c2f6c55e",
+ "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/35/06/d5ae11c90f89d1c10ca62a2b2f66",
+ "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/a1/e9/960e434759c45b623fba34725e64",
+ "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/dd/d0/f85582938a539d2d677d3e36b61b",
+ "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/8d/e9/3685ecf7b07f3e3568867da5536f"
}
\ No newline at end of file
diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml
index 62472471..da52b07d 100644
--- a/.idea/dictionaries/ericf.xml
+++ b/.idea/dictionaries/ericf.xml
@@ -746,6 +746,7 @@
gameplayer
gameport
gameresults
+ gamesettings
gameteam
gametype
gametypes
@@ -783,6 +784,7 @@
getpt
getremote
getscanresults
+ getscoreconfig
getsession
getsockname
getsound
@@ -1444,6 +1446,7 @@
prevstate
priceraw
printcolors
+ printf
printnodes
printobjects
printpaths
@@ -1640,6 +1643,7 @@
sched
sclx
scly
+ scoreconfig
scorescreen
scoreteam
scoretxt
diff --git a/assets/.asset_manifest_public.json b/assets/.asset_manifest_public.json
index 24552382..df9ec086 100644
--- a/assets/.asset_manifest_public.json
+++ b/assets/.asset_manifest_public.json
@@ -24,6 +24,7 @@
"ba_data/python/ba/__pycache__/_freeforallsession.cpython-37.opt-1.pyc",
"ba_data/python/ba/__pycache__/_gameactivity.cpython-37.opt-1.pyc",
"ba_data/python/ba/__pycache__/_gameresults.cpython-37.opt-1.pyc",
+ "ba_data/python/ba/__pycache__/_gamesettings.cpython-37.opt-1.pyc",
"ba_data/python/ba/__pycache__/_gameutils.cpython-37.opt-1.pyc",
"ba_data/python/ba/__pycache__/_general.cpython-37.opt-1.pyc",
"ba_data/python/ba/__pycache__/_hooks.cpython-37.opt-1.pyc",
@@ -80,6 +81,7 @@
"ba_data/python/ba/_freeforallsession.py",
"ba_data/python/ba/_gameactivity.py",
"ba_data/python/ba/_gameresults.py",
+ "ba_data/python/ba/_gamesettings.py",
"ba_data/python/ba/_gameutils.py",
"ba_data/python/ba/_general.py",
"ba_data/python/ba/_hooks.py",
diff --git a/assets/Makefile b/assets/Makefile
index b5f37459..1d120c97 100644
--- a/assets/Makefile
+++ b/assets/Makefile
@@ -166,6 +166,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
build/ba_data/python/ba/_freeforallsession.py \
build/ba_data/python/ba/_gameactivity.py \
build/ba_data/python/ba/_gameresults.py \
+ build/ba_data/python/ba/_gamesettings.py \
build/ba_data/python/ba/_gameutils.py \
build/ba_data/python/ba/_general.py \
build/ba_data/python/ba/_hooks.py \
@@ -396,6 +397,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
build/ba_data/python/ba/__pycache__/_freeforallsession.cpython-37.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_gameactivity.cpython-37.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_gameresults.cpython-37.opt-1.pyc \
+ build/ba_data/python/ba/__pycache__/_gamesettings.cpython-37.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_gameutils.cpython-37.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_general.cpython-37.opt-1.pyc \
build/ba_data/python/ba/__pycache__/_hooks.cpython-37.opt-1.pyc \
@@ -734,6 +736,11 @@ build/ba_data/python/ba/__pycache__/_gameresults.cpython-37.opt-1.pyc: \
@echo Compiling script: $^
@rm -rf $@ && $(TOOLS_DIR)/snippets compile_python_files $^ && chmod 444 $@
+build/ba_data/python/ba/__pycache__/_gamesettings.cpython-37.opt-1.pyc: \
+ build/ba_data/python/ba/_gamesettings.py
+ @echo Compiling script: $^
+ @rm -rf $@ && $(TOOLS_DIR)/snippets compile_python_files $^ && chmod 444 $@
+
build/ba_data/python/ba/__pycache__/_gameutils.cpython-37.opt-1.pyc: \
build/ba_data/python/ba/_gameutils.py
@echo Compiling script: $^
diff --git a/assets/src/ba_data/python/_ba.py b/assets/src/ba_data/python/_ba.py
index cea20518..667f6921 100644
--- a/assets/src/ba_data/python/_ba.py
+++ b/assets/src/ba_data/python/_ba.py
@@ -34,7 +34,7 @@ NOTE: This file was autogenerated by gendummymodule; do not edit by hand.
"""
# (hash we can use to see if this file is out of date)
-# SOURCES_HASH=78832523659898286100096175148597852751
+# SOURCES_HASH=48988468250135652281791058794704714718
# I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression
diff --git a/assets/src/ba_data/python/ba/__init__.py b/assets/src/ba_data/python/ba/__init__.py
index d3f41562..9f1c38b3 100644
--- a/assets/src/ba_data/python/ba/__init__.py
+++ b/assets/src/ba_data/python/ba/__init__.py
@@ -56,11 +56,14 @@ from ba._error import (
from ba._freeforallsession import FreeForAllSession
from ba._gameactivity import GameActivity
from ba._gameresults import TeamGameResults
+from ba._gamesettings import (Setting, IntSetting, FloatSetting, ChoiceSetting,
+ BoolSetting, IntChoiceSetting,
+ FloatChoiceSetting)
from ba._lang import Lstr, setlanguage, get_valid_languages
from ba._map import Map, getmaps
from ba._session import Session
from ba._servermode import ServerController
-from ba._score import ScoreType, ScoreInfo
+from ba._score import ScoreType, ScoreConfig
from ba._stats import PlayerScoredMessage, PlayerRecord, Stats
from ba._team import SessionTeam, Team, EmptyTeam
from ba._teamgame import TeamGameActivity
diff --git a/assets/src/ba_data/python/ba/_appdelegate.py b/assets/src/ba_data/python/ba/_appdelegate.py
index a1bcb9de..148b5587 100644
--- a/assets/src/ba_data/python/ba/_appdelegate.py
+++ b/assets/src/ba_data/python/ba/_appdelegate.py
@@ -36,9 +36,8 @@ class AppDelegate:
def create_default_game_settings_ui(
self, gameclass: Type[ba.GameActivity],
- sessionclass: Type[ba.Session], config: Optional[Dict[str, Any]],
- completion_call: Callable[[Optional[Dict[str, Any]]],
- None]) -> None:
+ sessionclass: Type[ba.Session], config: Optional[dict],
+ completion_call: Callable[[Optional[dict]], None]) -> None:
"""Launch a UI to configure the given game config.
It should manipulate the contents of config and call completion_call
diff --git a/assets/src/ba_data/python/ba/_coopgame.py b/assets/src/ba_data/python/ba/_coopgame.py
index 6bba3296..0895181d 100644
--- a/assets/src/ba_data/python/ba/_coopgame.py
+++ b/assets/src/ba_data/python/ba/_coopgame.py
@@ -121,7 +121,7 @@ class CoopGameActivity(GameActivity[PlayerType, TeamType]):
animate(txt.node, 'scale', {1.0: 0.0, 1.1: 0.7, 1.2: 0.6})
break
- # FIXME: this is now redundant with activityutils.get_score_info();
+ # FIXME: this is now redundant with activityutils.getscoreconfig();
# need to kill this.
def get_score_type(self) -> str:
"""
diff --git a/assets/src/ba_data/python/ba/_coopsession.py b/assets/src/ba_data/python/ba/_coopsession.py
index 22456491..ac2b4add 100644
--- a/assets/src/ba_data/python/ba/_coopsession.py
+++ b/assets/src/ba_data/python/ba/_coopsession.py
@@ -121,10 +121,10 @@ class CoopSession(Session):
settings = level.get_settings()
# Make sure all settings the game expects are present.
- neededsettings = gametype.get_game_settings(type(self))
- for settingname, setting in neededsettings:
- if settingname not in settings:
- settings[settingname] = setting['default']
+ neededsettings = gametype.get_available_settings(type(self))
+ for setting in neededsettings:
+ if setting.name not in settings:
+ settings[setting.name] = setting.default
newactivity = _ba.new_activity(gametype, settings)
assert isinstance(newactivity, GameActivity)
@@ -144,10 +144,10 @@ class CoopSession(Session):
settings = nextlevel.get_settings()
# Make sure all settings the game expects are present.
- neededsettings = gametype.get_game_settings(type(self))
- for settingname, setting in neededsettings:
- if settingname not in settings:
- settings[settingname] = setting['default']
+ neededsettings = gametype.get_available_settings(type(self))
+ for setting in neededsettings:
+ if setting.name not in settings:
+ settings[setting.name] = setting.default
# We wanna be in the activity's context while taking it down.
newactivity = _ba.new_activity(gametype, settings)
diff --git a/assets/src/ba_data/python/ba/_gameactivity.py b/assets/src/ba_data/python/ba/_gameactivity.py
index 188d599d..66f8d263 100644
--- a/assets/src/ba_data/python/ba/_gameactivity.py
+++ b/assets/src/ba_data/python/ba/_gameactivity.py
@@ -27,7 +27,7 @@ import random
from typing import TYPE_CHECKING, TypeVar
from ba._activity import Activity
-from ba._score import ScoreInfo
+from ba._score import ScoreConfig
from ba._lang import Lstr
from ba._messages import PlayerDiedMessage, StandMessage
from ba._error import NotFoundError, print_error, print_exception
@@ -62,11 +62,11 @@ class GameActivity(Activity[PlayerType, TeamType]):
# Default get_description() will return this if not None.
description: Optional[str] = None
- # Default get_game_settings() will return this if not None.
- game_settings: Optional[List[Tuple[str, Dict[str, Any]]]] = None
+ # Default get_available_settings() will return this if not None.
+ available_settings: Optional[List[ba.Setting]] = None
- # Default get_score_info() will return this if not None.
- score_info: Optional[ba.ScoreInfo] = None
+ # Default getscoreconfig() will return this if not None.
+ scoreconfig: Optional[ba.ScoreConfig] = None
# Override some defaults.
allow_pausing = True
@@ -83,8 +83,8 @@ class GameActivity(Activity[PlayerType, TeamType]):
def create_settings_ui(
cls,
sessionclass: Type[ba.Session],
- settings: Optional[Dict[str, Any]],
- completion_call: Callable[[Optional[Dict[str, Any]]], None],
+ settings: Optional[dict],
+ completion_call: Callable[[Optional[dict]], None],
) -> None:
"""Launch an in-game UI to configure settings for a game type.
@@ -93,11 +93,11 @@ class GameActivity(Activity[PlayerType, TeamType]):
'config' should be an existing config dict (specifies 'edit' ui mode)
or None (specifies 'add' ui mode).
- 'completion_call' will be called with a filled-out config dict on
+ 'completion_call' will be called with a filled-out settings dict on
success or None on cancel.
Generally subclasses don't need to override this; if they override
- ba.GameActivity.get_game_settings() and
+ ba.GameActivity.get_available_settings() and
ba.GameActivity.get_supported_maps() they can just rely on
the default implementation here which calls those methods.
"""
@@ -107,9 +107,10 @@ class GameActivity(Activity[PlayerType, TeamType]):
completion_call)
@classmethod
- def get_score_info(cls) -> ba.ScoreInfo:
+ def getscoreconfig(cls) -> ba.ScoreConfig:
"""Return info about game scoring setup; can be overridden by games."""
- return cls.score_info if cls.score_info is not None else ScoreInfo()
+ return cls.scoreconfig if cls.scoreconfig is not None else ScoreConfig(
+ )
@classmethod
def getname(cls) -> str:
@@ -129,7 +130,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
# A few substitutions for 'Epic', 'Solo' etc. modes.
# FIXME: Should provide a way for game types to define filters of
- # their own.
+ # their own and should not rely on hard-coded settings names.
if settings is not None:
if 'Solo Mode' in settings and settings['Solo Mode']:
name = Lstr(resource='soloNameFilterText',
@@ -167,9 +168,8 @@ class GameActivity(Activity[PlayerType, TeamType]):
return Lstr(translate=('gameDescriptions', description))
@classmethod
- def get_game_settings(
- cls,
- sessiontype: Type[ba.Session]) -> List[Tuple[str, Dict[str, Any]]]:
+ def get_available_settings(
+ cls, sessiontype: Type[ba.Session]) -> List[ba.Setting]:
"""
Called by the default ba.GameActivity.create_settings_ui()
implementation; should return a dict of config options to be presented
@@ -191,9 +191,9 @@ class GameActivity(Activity[PlayerType, TeamType]):
'increment': Value increment for int/float settings.
- # example get_game_settings() for a capture-the-flag game:
+ # example get_available_settings() for a capture-the-flag game:
@classmethod
- def get_game_settings(cls, sessiontype):
+ def get_available_settings(cls, sessiontype):
return [("Score to Win", {
'default': 3,
'min_value': 1
@@ -228,7 +228,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
})]
"""
del sessiontype # Unused arg.
- return [] if cls.game_settings is None else cls.game_settings
+ return [] if cls.available_settings is None else cls.available_settings
@classmethod
def get_supported_maps(cls, sessiontype: Type[ba.Session]) -> List[str]:
diff --git a/assets/src/ba_data/python/ba/_gameresults.py b/assets/src/ba_data/python/ba/_gameresults.py
index c7009ff4..58e0a285 100644
--- a/assets/src/ba_data/python/ba/_gameresults.py
+++ b/assets/src/ba_data/python/ba/_gameresults.py
@@ -68,12 +68,12 @@ class TeamGameResults:
raise RuntimeError('Game set twice for TeamGameResults.')
self._game_set = True
self._sessionteams = [weakref.ref(team) for team in game.teams]
- score_info = game.get_score_info()
+ scoreconfig = game.getscoreconfig()
self._playerinfos = copy.deepcopy(game.initialplayerinfos)
- self._lower_is_better = score_info.lower_is_better
- self._score_label = score_info.label
- self._none_is_winner = score_info.none_is_winner
- self._scoretype = score_info.scoretype
+ self._lower_is_better = scoreconfig.lower_is_better
+ self._score_label = scoreconfig.label
+ self._none_is_winner = scoreconfig.none_is_winner
+ self._scoretype = scoreconfig.scoretype
def set_team_score(self, team: ba.Team, score: Optional[int]) -> None:
"""Set the score for a given ba.Team.
diff --git a/assets/src/ba_data/python/ba/_gamesettings.py b/assets/src/ba_data/python/ba/_gamesettings.py
new file mode 100644
index 00000000..260ad63f
--- /dev/null
+++ b/assets/src/ba_data/python/ba/_gamesettings.py
@@ -0,0 +1,102 @@
+# Copyright (c) 2011-2020 Eric Froemling
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# -----------------------------------------------------------------------------
+"""Functionality for user-controllable settings."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+from dataclasses import dataclass
+
+if TYPE_CHECKING:
+ from typing import Any, List, Tuple
+
+
+@dataclass
+class Setting:
+ """Defines a user-controllable setting for a game or other entity.
+
+ Category: Gameplay Classes
+ """
+
+ name: str
+ default: Any
+
+
+@dataclass
+class BoolSetting(Setting):
+ """A boolean game setting.
+
+ Category: Settings Classes
+ """
+ default: bool
+
+
+@dataclass
+class IntSetting(Setting):
+ """An integer game setting.
+
+ Category: Settings Classes
+ """
+ default: int
+ min_value: int = 0
+ max_value: int = 9999
+ increment: int = 1
+
+
+@dataclass
+class FloatSetting(Setting):
+ """A floating point game setting.
+
+ Category: Settings Classes
+ """
+ default: float
+ min_value: float = 0.0
+ max_value: float = 9999.0
+ increment: float = 1.0
+
+
+@dataclass
+class ChoiceSetting(Setting):
+ """A setting with multiple choices.
+
+ Category: Settings Classes
+ """
+ choices: List[Tuple[str, Any]]
+
+
+@dataclass
+class IntChoiceSetting(ChoiceSetting):
+ """An int setting with multiple choices.
+
+ Category: Settings Classes
+ """
+ default: int
+ choices: List[Tuple[str, int]]
+
+
+@dataclass
+class FloatChoiceSetting(ChoiceSetting):
+ """A float setting with multiple choices.
+
+ Category: Settings Classes
+ """
+ default: float
+ choices: List[Tuple[str, float]]
diff --git a/assets/src/ba_data/python/ba/_level.py b/assets/src/ba_data/python/ba/_level.py
index 05d4f709..ffe88bde 100644
--- a/assets/src/ba_data/python/ba/_level.py
+++ b/assets/src/ba_data/python/ba/_level.py
@@ -144,7 +144,7 @@ class Level:
can be changed to separate its new high score lists/etc. from the old.
"""
if self._score_version_string is None:
- scorever = self._gametype.get_score_info().version
+ scorever = self._gametype.getscoreconfig().version
if scorever != '':
scorever = ' ' + scorever
self._score_version_string = scorever
diff --git a/assets/src/ba_data/python/ba/_playlist.py b/assets/src/ba_data/python/ba/_playlist.py
index 22c18c1b..29dc896f 100644
--- a/assets/src/ba_data/python/ba/_playlist.py
+++ b/assets/src/ba_data/python/ba/_playlist.py
@@ -151,11 +151,10 @@ def filter_playlist(playlist: PlaylistType,
entry['is_unowned_game'] = True
# Make sure all settings the game defines are present.
- neededsettings = gameclass.get_game_settings(sessiontype)
- for setting_name, setting in neededsettings:
- if (setting_name not in entry['settings']
- and 'default' in setting):
- entry['settings'][setting_name] = setting['default']
+ neededsettings = gameclass.get_available_settings(sessiontype)
+ for setting in neededsettings:
+ if setting.name not in entry['settings']:
+ entry['settings'][setting.name] = setting.default
goodlist.append(entry)
except ImportError as exc:
print(f'Import failed while scanning playlist: {exc}')
diff --git a/assets/src/ba_data/python/ba/_score.py b/assets/src/ba_data/python/ba/_score.py
index 83432b0f..79326f7c 100644
--- a/assets/src/ba_data/python/ba/_score.py
+++ b/assets/src/ba_data/python/ba/_score.py
@@ -42,8 +42,8 @@ class ScoreType(Enum):
@dataclass
-class ScoreInfo:
- """Info about a game's scoring setup.
+class ScoreConfig:
+ """Settings for how a game handles scores.
Category: Gameplay Classes
diff --git a/assets/src/ba_data/python/bastd/appdelegate.py b/assets/src/ba_data/python/bastd/appdelegate.py
index 11b145d4..c3e19ee0 100644
--- a/assets/src/ba_data/python/bastd/appdelegate.py
+++ b/assets/src/ba_data/python/bastd/appdelegate.py
@@ -34,9 +34,8 @@ class AppDelegate(ba.AppDelegate):
def create_default_game_settings_ui(
self, gameclass: Type[ba.GameActivity],
- sessionclass: Type[ba.Session], config: Optional[Dict[str, Any]],
- completion_call: Callable[[Optional[Dict[str, Any]]],
- Any]) -> None:
+ sessionclass: Type[ba.Session], config: Optional[dict],
+ completion_call: Callable[[Optional[dict]], Any]) -> None:
"""(internal)"""
# Replace the main window once we come up successfully.
diff --git a/assets/src/ba_data/python/bastd/game/assault.py b/assets/src/ba_data/python/bastd/game/assault.py
index 3aa609fa..d1226977 100644
--- a/assets/src/ba_data/python/bastd/game/assault.py
+++ b/assets/src/ba_data/python/bastd/game/assault.py
@@ -57,25 +57,36 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
name = 'Assault'
description = 'Reach the enemy flag to score.'
- game_settings = [
- ('Score to Win', {
- 'min_value': 1,
- 'default': 3
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
- ('Epic Mode', {
- 'default': False
- }),
+ available_settings = [
+ ba.IntSetting(
+ 'Score to Win',
+ min_value=1,
+ default=3,
+ ),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
+ ba.BoolSetting('Epic Mode', default=False),
]
@classmethod
diff --git a/assets/src/ba_data/python/bastd/game/capturetheflag.py b/assets/src/ba_data/python/bastd/game/capturetheflag.py
index 44ae5bb9..3f709d0b 100644
--- a/assets/src/ba_data/python/bastd/game/capturetheflag.py
+++ b/assets/src/ba_data/python/bastd/game/capturetheflag.py
@@ -114,35 +114,44 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
name = 'Capture the Flag'
description = 'Return the enemy flag to score.'
- game_settings = [
- ('Score to Win', {
- 'min_value': 1,
- 'default': 3
- }),
- ('Flag Touch Return Time', {
- 'min_value': 0,
- 'default': 0,
- 'increment': 1
- }),
- ('Flag Idle Return Time', {
- 'min_value': 5,
- 'default': 30,
- 'increment': 5
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
- ('Epic Mode', {
- 'default': False
- }),
+ available_settings = [
+ ba.IntSetting('Score to Win', min_value=1, default=3),
+ ba.IntSetting(
+ 'Flag Touch Return Time',
+ min_value=0,
+ default=0,
+ increment=1,
+ ),
+ ba.IntSetting(
+ 'Flag Idle Return Time',
+ min_value=5,
+ default=30,
+ increment=5,
+ ),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
+ ba.BoolSetting('Epic Mode', default=False),
]
@classmethod
diff --git a/assets/src/ba_data/python/bastd/game/chosenone.py b/assets/src/ba_data/python/bastd/game/chosenone.py
index dd6cb3dd..95fc6527 100644
--- a/assets/src/ba_data/python/bastd/game/chosenone.py
+++ b/assets/src/ba_data/python/bastd/game/chosenone.py
@@ -62,34 +62,41 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
name = 'Chosen One'
description = ('Be the chosen one for a length of time to win.\n'
'Kill the chosen one to become it.')
- game_settings = [
- ('Chosen One Time', {
- 'min_value': 10,
- 'default': 30,
- 'increment': 10
- }),
- ('Chosen One Gets Gloves', {
- 'default': True
- }),
- ('Chosen One Gets Shield', {
- 'default': False
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
- ('Epic Mode', {
- 'default': False
- }),
+ available_settings = [
+ ba.IntSetting(
+ 'Chosen One Time',
+ min_value=10,
+ default=30,
+ increment=10,
+ ),
+ ba.BoolSetting('Chosen One Gets Gloves', default=True),
+ ba.BoolSetting('Chosen One Gets Shield', default=False),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
+ ba.BoolSetting('Epic Mode', default=False),
]
- score_info = ba.ScoreInfo(label='Time Held')
+ scoreconfig = ba.ScoreConfig(label='Time Held')
@classmethod
def get_supported_maps(cls, sessiontype: Type[ba.Session]) -> List[str]:
diff --git a/assets/src/ba_data/python/bastd/game/conquest.py b/assets/src/ba_data/python/bastd/game/conquest.py
index 3a40e783..7849910b 100644
--- a/assets/src/ba_data/python/bastd/game/conquest.py
+++ b/assets/src/ba_data/python/bastd/game/conquest.py
@@ -95,21 +95,31 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
name = 'Conquest'
description = 'Secure all flags on the map to win.'
- game_settings = [
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
- ('Epic Mode', {
- 'default': False
- }),
+ available_settings = [
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
+ ba.BoolSetting('Epic Mode', default=False),
]
@classmethod
diff --git a/assets/src/ba_data/python/bastd/game/deathmatch.py b/assets/src/ba_data/python/bastd/game/deathmatch.py
index 2ce26015..0b29ef1f 100644
--- a/assets/src/ba_data/python/bastd/game/deathmatch.py
+++ b/assets/src/ba_data/python/bastd/game/deathmatch.py
@@ -57,29 +57,39 @@ class DeathMatchGame(ba.TeamGameActivity[Player, Team]):
announce_player_deaths = True
@classmethod
- def get_game_settings(
- cls,
- sessiontype: Type[ba.Session]) -> List[Tuple[str, Dict[str, Any]]]:
- settings: List[Tuple[str, Dict[str, Any]]] = [
- ('Kills to Win Per Player', {
- 'min_value': 1,
- 'default': 5,
- 'increment': 1
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
- ('Epic Mode', {
- 'default': False
- }),
+ def get_available_settings(
+ cls, sessiontype: Type[ba.Session]) -> List[ba.Setting]:
+ settings = [
+ ba.IntSetting(
+ 'Kills to Win Per Player',
+ min_value=1,
+ default=5,
+ increment=1,
+ ),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
+ ba.BoolSetting('Epic Mode', default=False),
]
# In teams mode, a suicide gives a point to the other team, but in
@@ -88,7 +98,8 @@ class DeathMatchGame(ba.TeamGameActivity[Player, Team]):
# be able to go negative. (to avoid a strategy of just
# suiciding until you get a good drop)
if issubclass(sessiontype, ba.FreeForAllSession):
- settings.append(('Allow Negative Scores', {'default': False}))
+ settings.append(
+ ba.BoolSetting('Allow Negative Scores', default=False))
return settings
@@ -150,7 +161,7 @@ class DeathMatchGame(ba.TeamGameActivity[Player, Team]):
killer = msg.getkillerplayer(Player)
if killer is None:
- return
+ return None
# Handle team-kills.
if killer.team is player.team:
@@ -191,7 +202,8 @@ class DeathMatchGame(ba.TeamGameActivity[Player, Team]):
ba.timer(0.5, self.end_game)
else:
- super().handlemessage(msg)
+ return super().handlemessage(msg)
+ return None
def _update_scoreboard(self) -> None:
for team in self.teams:
diff --git a/assets/src/ba_data/python/bastd/game/easteregghunt.py b/assets/src/ba_data/python/bastd/game/easteregghunt.py
index 9d1b98cb..bc37fcca 100644
--- a/assets/src/ba_data/python/bastd/game/easteregghunt.py
+++ b/assets/src/ba_data/python/bastd/game/easteregghunt.py
@@ -62,8 +62,8 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
name = 'Easter Egg Hunt'
description = 'Gather eggs!'
- game_settings = [('Pro Mode', {'default': False})]
- score_info = ba.ScoreInfo(label='Score', scoretype=ba.ScoreType.POINTS)
+ available_settings = [ba.BoolSetting('Pro Mode', default=False)]
+ scoreconfig = ba.ScoreConfig(label='Score', scoretype=ba.ScoreType.POINTS)
# We're currently hard-coded for one map.
@classmethod
diff --git a/assets/src/ba_data/python/bastd/game/elimination.py b/assets/src/ba_data/python/bastd/game/elimination.py
index b6bbae25..3ab82c96 100644
--- a/assets/src/ba_data/python/bastd/game/elimination.py
+++ b/assets/src/ba_data/python/bastd/game/elimination.py
@@ -191,41 +191,52 @@ class EliminationGame(ba.TeamGameActivity[Player, Team]):
name = 'Elimination'
description = 'Last remaining alive wins.'
- score_info = ba.ScoreInfo(label='Survived',
- scoretype=ba.ScoreType.SECONDS,
- none_is_winner=True)
+ scoreconfig = ba.ScoreConfig(label='Survived',
+ scoretype=ba.ScoreType.SECONDS,
+ none_is_winner=True)
# Show messages when players die since it's meaningful here.
announce_player_deaths = True
@classmethod
- def get_game_settings(
- cls,
- sessiontype: Type[ba.Session]) -> List[Tuple[str, Dict[str, Any]]]:
- settings: List[Tuple[str, Dict[str, Any]]] = [
- ('Lives Per Player', {
- 'default': 1,
- 'min_value': 1,
- 'max_value': 10,
- 'increment': 1
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
- ('Epic Mode', {
- 'default': False
- }),
+ def get_available_settings(
+ cls, sessiontype: Type[ba.Session]) -> List[ba.Setting]:
+ settings = [
+ ba.IntSetting(
+ 'Lives Per Player',
+ default=1,
+ min_value=1,
+ max_value=10,
+ increment=1,
+ ),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
+ ba.BoolSetting('Epic Mode', default=False),
]
if issubclass(sessiontype, ba.DualTeamSession):
- settings.append(('Solo Mode', {'default': False}))
- settings.append(('Balance Total Lives', {'default': False}))
+ settings.append(ba.BoolSetting('Solo Mode', default=False))
+ settings.append(
+ ba.BoolSetting('Balance Total Lives', default=False))
return settings
@classmethod
diff --git a/assets/src/ba_data/python/bastd/game/football.py b/assets/src/ba_data/python/bastd/game/football.py
index 3d8abc67..530ea8ef 100644
--- a/assets/src/ba_data/python/bastd/game/football.py
+++ b/assets/src/ba_data/python/bastd/game/football.py
@@ -94,23 +94,36 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
name = 'Football'
description = 'Get the flag to the enemy end zone.'
- game_settings = [
- ('Score to Win', {
- 'min_value': 7,
- 'default': 21,
- 'increment': 7
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
+ available_settings = [
+ ba.IntSetting(
+ 'Score to Win',
+ min_value=7,
+ default=21,
+ increment=7,
+ ),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
]
default_music = ba.MusicType.FOOTBALL
@@ -333,10 +346,11 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
name = 'Football'
tips = ['Use the pick-up button to grab the flag < ${PICKUP} >']
- score_info = ba.ScoreInfo(scoretype=ba.ScoreType.MILLISECONDS, version='B')
+ scoreconfig = ba.ScoreConfig(scoretype=ba.ScoreType.MILLISECONDS,
+ version='B')
default_music = ba.MusicType.FOOTBALL
- # FIXME: Need to update co-op games to use get_score_info.
+ # FIXME: Need to update co-op games to use getscoreconfig.
def get_score_type(self) -> str:
return 'time'
diff --git a/assets/src/ba_data/python/bastd/game/hockey.py b/assets/src/ba_data/python/bastd/game/hockey.py
index 57a17ec0..30a64f2b 100644
--- a/assets/src/ba_data/python/bastd/game/hockey.py
+++ b/assets/src/ba_data/python/bastd/game/hockey.py
@@ -125,23 +125,36 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
name = 'Hockey'
description = 'Score some goals.'
- game_settings = [
- ('Score to Win', {
- 'min_value': 1,
- 'default': 1,
- 'increment': 1
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
+ available_settings = [
+ ba.IntSetting(
+ 'Score to Win',
+ min_value=1,
+ default=1,
+ increment=1,
+ ),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
]
default_music = ba.MusicType.HOCKEY
diff --git a/assets/src/ba_data/python/bastd/game/keepaway.py b/assets/src/ba_data/python/bastd/game/keepaway.py
index 9ae4600a..79007e6d 100644
--- a/assets/src/ba_data/python/bastd/game/keepaway.py
+++ b/assets/src/ba_data/python/bastd/game/keepaway.py
@@ -64,25 +64,38 @@ class KeepAwayGame(ba.TeamGameActivity[Player, Team]):
name = 'Keep Away'
description = 'Carry the flag for a set length of time.'
- game_settings = [
- ('Hold Time', {
- 'min_value': 10,
- 'default': 30,
- 'increment': 10
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
+ available_settings = [
+ ba.IntSetting(
+ 'Hold Time',
+ min_value=10,
+ default=30,
+ increment=10,
+ ),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
]
- score_info = ba.ScoreInfo(label='Time Held')
+ scoreconfig = ba.ScoreConfig(label='Time Held')
default_music = ba.MusicType.KEEP_AWAY
@classmethod
diff --git a/assets/src/ba_data/python/bastd/game/kingofthehill.py b/assets/src/ba_data/python/bastd/game/kingofthehill.py
index 8d98b26e..5e44602e 100644
--- a/assets/src/ba_data/python/bastd/game/kingofthehill.py
+++ b/assets/src/ba_data/python/bastd/game/kingofthehill.py
@@ -68,25 +68,38 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
name = 'King of the Hill'
description = 'Secure the flag for a set length of time.'
- game_settings = [
- ('Hold Time', {
- 'min_value': 10,
- 'default': 30,
- 'increment': 10
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Respawn Times', {
- 'choices': [('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0),
- ('Long', 2.0), ('Longer', 4.0)],
- 'default': 1.0
- }),
+ available_settings = [
+ ba.IntSetting(
+ 'Hold Time',
+ min_value=10,
+ default=30,
+ increment=10,
+ ),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ default=0,
+ ),
+ ba.FloatChoiceSetting(
+ 'Respawn Times',
+ choices=[
+ ('Shorter', 0.25),
+ ('Short', 0.5),
+ ('Normal', 1.0),
+ ('Long', 2.0),
+ ('Longer', 4.0),
+ ],
+ default=1.0,
+ ),
]
- score_info = ba.ScoreInfo(label='Time Held')
+ scoreconfig = ba.ScoreConfig(label='Time Held')
@classmethod
def supports_session_type(cls, sessiontype: Type[ba.Session]) -> bool:
diff --git a/assets/src/ba_data/python/bastd/game/meteorshower.py b/assets/src/ba_data/python/bastd/game/meteorshower.py
index 7512a186..5f7df16f 100644
--- a/assets/src/ba_data/python/bastd/game/meteorshower.py
+++ b/assets/src/ba_data/python/bastd/game/meteorshower.py
@@ -54,10 +54,10 @@ class MeteorShowerGame(ba.TeamGameActivity[Player, Team]):
name = 'Meteor Shower'
description = 'Dodge the falling bombs.'
- game_settings = [('Epic Mode', {'default': False})]
- score_info = ba.ScoreInfo(label='Survived',
- scoretype=ba.ScoreType.MILLISECONDS,
- version='B')
+ available_settings = [ba.BoolSetting('Epic Mode', default=False)]
+ scoreconfig = ba.ScoreConfig(label='Survived',
+ scoretype=ba.ScoreType.MILLISECONDS,
+ version='B')
# Print messages when players die (since its meaningful in this game).
announce_player_deaths = True
diff --git a/assets/src/ba_data/python/bastd/game/ninjafight.py b/assets/src/ba_data/python/bastd/game/ninjafight.py
index c2187f80..54e0a20d 100644
--- a/assets/src/ba_data/python/bastd/game/ninjafight.py
+++ b/assets/src/ba_data/python/bastd/game/ninjafight.py
@@ -53,9 +53,9 @@ class NinjaFightGame(ba.TeamGameActivity[Player, Team]):
name = 'Ninja Fight'
description = 'How fast can you defeat the ninjas?'
- score_info = ba.ScoreInfo(label='Time',
- scoretype=ba.ScoreType.MILLISECONDS,
- lower_is_better=True)
+ scoreconfig = ba.ScoreConfig(label='Time',
+ scoretype=ba.ScoreType.MILLISECONDS,
+ lower_is_better=True)
@classmethod
def get_supported_maps(cls, sessiontype: Type[ba.Session]) -> List[str]:
diff --git a/assets/src/ba_data/python/bastd/game/race.py b/assets/src/ba_data/python/bastd/game/race.py
index be5d0ec5..9b050b96 100644
--- a/assets/src/ba_data/python/bastd/game/race.py
+++ b/assets/src/ba_data/python/bastd/game/race.py
@@ -95,45 +95,55 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
name = 'Race'
description = 'Run real fast!'
- score_info = ba.ScoreInfo(label='Time',
- lower_is_better=True,
- scoretype=ba.ScoreType.MILLISECONDS)
+ scoreconfig = ba.ScoreConfig(label='Time',
+ lower_is_better=True,
+ scoretype=ba.ScoreType.MILLISECONDS)
@classmethod
- def get_game_settings(
- cls,
- sessiontype: Type[ba.Session]) -> List[Tuple[str, Dict[str, Any]]]:
- settings: List[Tuple[str, Dict[str, Any]]] = [
- ('Laps', {
- 'min_value': 1,
- 'default': 3,
- 'increment': 1
- }),
- ('Time Limit', {
- 'choices': [('None', 0), ('1 Minute', 60), ('2 Minutes', 120),
- ('5 Minutes', 300), ('10 Minutes', 600),
- ('20 Minutes', 1200)],
- 'default': 0
- }),
- ('Mine Spawning', {
- 'choices': [('No Mines', 0), ('8 Seconds', 8000),
- ('4 Seconds', 4000), ('2 Seconds', 2000)],
- 'default': 4000
- }),
- ('Bomb Spawning', {
- 'choices': [('None', 0), ('8 Seconds', 8000),
- ('4 Seconds', 4000), ('2 Seconds', 2000),
- ('1 Second', 1000)],
- 'default': 2000
- }),
- ('Epic Mode', {
- 'default': False
- }),
+ def get_available_settings(
+ cls, sessiontype: Type[ba.Session]) -> List[ba.Setting]:
+ settings = [
+ ba.IntSetting('Laps', min_value=1, default=3, increment=1),
+ ba.IntChoiceSetting(
+ 'Time Limit',
+ default=0,
+ choices=[
+ ('None', 0),
+ ('1 Minute', 60),
+ ('2 Minutes', 120),
+ ('5 Minutes', 300),
+ ('10 Minutes', 600),
+ ('20 Minutes', 1200),
+ ],
+ ),
+ ba.IntChoiceSetting(
+ 'Mine Spawning',
+ default=4000,
+ choices=[
+ ('No Mines', 0),
+ ('8 Seconds', 8000),
+ ('4 Seconds', 4000),
+ ('2 Seconds', 2000),
+ ],
+ ),
+ ba.IntChoiceSetting(
+ 'Bomb Spawning',
+ choices=[
+ ('None', 0),
+ ('8 Seconds', 8000),
+ ('4 Seconds', 4000),
+ ('2 Seconds', 2000),
+ ('1 Second', 1000),
+ ],
+ default=2000,
+ ),
+ ba.BoolSetting('Epic Mode', default=False),
]
# We have some specific settings in teams mode.
if issubclass(sessiontype, ba.DualTeamSession):
- settings.append(('Entire Team Must Finish', {'default': False}))
+ settings.append(
+ ba.BoolSetting('Entire Team Must Finish', default=False))
return settings
@classmethod
diff --git a/assets/src/ba_data/python/bastd/game/targetpractice.py b/assets/src/ba_data/python/bastd/game/targetpractice.py
index 632ddc46..c646a6af 100644
--- a/assets/src/ba_data/python/bastd/game/targetpractice.py
+++ b/assets/src/ba_data/python/bastd/game/targetpractice.py
@@ -59,17 +59,10 @@ class TargetPracticeGame(ba.TeamGameActivity[Player, Team]):
name = 'Target Practice'
description = 'Bomb as many targets as you can.'
- game_settings = [
- ('Target Count', {
- 'min_value': 1,
- 'default': 3
- }),
- ('Enable Impact Bombs', {
- 'default': True
- }),
- ('Enable Triple Bombs', {
- 'default': True
- }),
+ available_settings = [
+ ba.IntSetting('Target Count', min_value=1, default=3),
+ ba.BoolSetting('Enable Impact Bombs', default=True),
+ ba.BoolSetting('Enable Triple Bombs', default=True)
]
default_music = ba.MusicType.FORWARD_MARCH
diff --git a/assets/src/ba_data/python/bastd/ui/playlist/editgame.py b/assets/src/ba_data/python/bastd/ui/playlist/editgame.py
index cb0b84bb..6486be14 100644
--- a/assets/src/ba_data/python/bastd/ui/playlist/editgame.py
+++ b/assets/src/ba_data/python/bastd/ui/playlist/editgame.py
@@ -72,7 +72,7 @@ class PlaylistEditGameWindow(ba.Window):
ba.screenmessage(ba.Lstr(resource='noValidMapsErrorText'))
raise Exception('No valid maps')
- self._settings_defs = gameclass.get_game_settings(sessiontype)
+ self._settings_defs = gameclass.get_available_settings(sessiontype)
self._completion_call = completion_call
# To start with, pick a random map out of the ones we own.
@@ -239,30 +239,31 @@ class PlaylistEditGameWindow(ba.Window):
v_align='center')
v -= map_height
- for setting_name, setting in self._settings_defs:
- value = setting['default']
+ for setting in self._settings_defs:
+ value = setting.default
value_type = type(value)
# Now, if there's an existing value for it in the config,
# override with that.
try:
if (config is not None and 'settings' in config
- and setting_name in config['settings']):
- value = value_type(config['settings'][setting_name])
+ and setting.name in config['settings']):
+ value = value_type(config['settings'][setting.name])
except Exception:
ba.print_exception()
# Shove the starting value in there to start.
- self._settings[setting_name] = value
+ self._settings[setting.name] = value
- name_translated = self._get_localized_setting_name(setting_name)
+ name_translated = self._get_localized_setting_name(setting.name)
mw1 = 280
mw2 = 70
# Handle types with choices specially:
- if 'choices' in setting:
- for choice in setting['choices']:
+ if isinstance(setting, ba.ChoiceSetting):
+ # if 'choices' in setting:
+ for choice in setting.choices:
if len(choice) != 2:
raise ValueError(
"Expected 2-member tuples for 'choices'; got: " +
@@ -281,10 +282,10 @@ class PlaylistEditGameWindow(ba.Window):
'got: ' + repr(setting))
# Start at the choice corresponding to the default if possible.
- self._choice_selections[setting_name] = 0
- for index, choice in enumerate(setting['choices']):
+ self._choice_selections[setting.name] = 0
+ for index, choice in enumerate(setting.choices):
if choice[1] == value:
- self._choice_selections[setting_name] = index
+ self._choice_selections[setting.name] = index
break
v -= spacing
@@ -300,8 +301,8 @@ class PlaylistEditGameWindow(ba.Window):
parent=self._subcontainer,
position=(h + 509 - 95, v),
size=(0, 28),
- text=self._get_localized_setting_name(setting['choices'][
- self._choice_selections[setting_name]][0]),
+ text=self._get_localized_setting_name(setting.choices[
+ self._choice_selections[setting.name]][0]),
editable=False,
color=(0.6, 1.0, 0.6, 1.0),
maxwidth=mw2,
@@ -314,7 +315,7 @@ class PlaylistEditGameWindow(ba.Window):
label='<',
autoselect=True,
on_activate_call=ba.Call(
- self._choice_inc, setting_name, txt,
+ self._choice_inc, setting.name, txt,
setting, -1),
repeat=True)
btn2 = ba.buttonwidget(parent=self._subcontainer,
@@ -323,25 +324,16 @@ class PlaylistEditGameWindow(ba.Window):
label='>',
autoselect=True,
on_activate_call=ba.Call(
- self._choice_inc, setting_name, txt,
+ self._choice_inc, setting.name, txt,
setting, 1),
repeat=True)
widget_column.append([btn1, btn2])
- elif value_type in [int, float]:
+ elif isinstance(setting, (ba.IntSetting, ba.FloatSetting)):
v -= spacing
- try:
- min_value = setting['min_value']
- except Exception:
- min_value = 0
- try:
- max_value = setting['max_value']
- except Exception:
- max_value = 9999
- try:
- increment = setting['increment']
- except Exception:
- increment = 1
+ min_value = setting.min_value
+ max_value = setting.max_value
+ increment = setting.increment
ba.textwidget(parent=self._subcontainer,
position=(h + 50, v),
size=(100, 30),
@@ -368,7 +360,7 @@ class PlaylistEditGameWindow(ba.Window):
on_activate_call=ba.Call(
self._inc, txt, min_value,
max_value, -increment, value_type,
- setting_name),
+ setting.name),
repeat=True)
btn2 = ba.buttonwidget(parent=self._subcontainer,
position=(h + 509 + 5, v),
@@ -378,7 +370,7 @@ class PlaylistEditGameWindow(ba.Window):
on_activate_call=ba.Call(
self._inc, txt, min_value,
max_value, increment, value_type,
- setting_name),
+ setting.name),
repeat=True)
widget_column.append([btn1, btn2])
@@ -413,7 +405,7 @@ class PlaylistEditGameWindow(ba.Window):
value=value,
on_value_change_call=ba.Call(
self._check_value_change,
- setting_name, txt))
+ setting.name, txt))
widget_column.append([cbw])
else:
@@ -463,8 +455,8 @@ class PlaylistEditGameWindow(ba.Window):
self._completion_call).get_root_widget()
def _choice_inc(self, setting_name: str, widget: ba.Widget,
- setting: Dict[str, Any], increment: int) -> None:
- choices = setting['choices']
+ setting: ba.ChoiceSetting, increment: int) -> None:
+ choices = setting.choices
if increment > 0:
self._choice_selections[setting_name] = min(
len(choices) - 1, self._choice_selections[setting_name] + 1)
diff --git a/docs/ba_module.md b/docs/ba_module.md
index 12344a28..1ecb47b6 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -1,5 +1,5 @@
-last updated on 2020-06-02 for Ballistica version 1.5.0 build 20041
+last updated on 2020-06-02 for Ballistica version 1.5.0 build 20042
This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please let me know. Happy modding!
@@ -31,7 +31,7 @@
ba.PlayerInfo
ba.PlayerRecord
- ba.ScoreInfo
+ ba.ScoreConfig
ba.Session
ba.SessionPlayer
ba.SessionTeam
+ ba.Setting
ba.StandLocation
ba.Stats
ba.Team
@@ -220,6 +221,17 @@
+
+
<top level class>
@@ -1087,7 +1099,7 @@ manually.
Methods:
-
-
create_default_game_settings_ui(self, gameclass: Type[ba.GameActivity], sessionclass: Type[ba.Session], config: Optional[Dict[str, Any]], completion_call: Callable[[Optional[Dict[str, Any]]], None]) -> None
+create_default_game_settings_ui(self, gameclass: Type[ba.GameActivity], sessionclass: Type[ba.Session], config: Optional[dict], completion_call: Callable[[Optional[dict]], None]) -> None
Launch a UI to configure the given game config.
@@ -1161,6 +1173,21 @@ when done.
Behavior is similar to ba.gettexture()
+
+
+
+Inherits from: ba.Setting
+A boolean game setting.
+
+Category: Settings Classes
+
+
+Methods:
+
+-
+
ba.BoolSetting(name: str, default: bool)
+
@@ -1291,6 +1318,21 @@ mycall()
ba.CelebrateMessage(duration: float = 10.0)
+
+
+
+Inherits from: ba.Setting
+A setting with multiple choices.
+
+Category: Settings Classes
+
+
+Methods:
+
+-
+
ba.ChoiceSetting(name: str, default: Any, choices: List[Tuple[str, Any]])
+
@@ -1617,7 +1659,7 @@ start_long_action(callback_when_done=ba.ContextC
Methods Inherited:
-
+
Methods Defined or Overridden:
@@ -2188,6 +2230,36 @@ its time with lingering corpses, sound effects, etc.
Whether this object exists.
+
+
+
+Inherits from: ba.ChoiceSetting, ba.Setting
+A float setting with multiple choices.
+
+Category: Settings Classes
+
+
+Methods:
+
+-
+
ba.FloatChoiceSetting(name: str, default: float, choices: List[Tuple[str, float]])
+
+
+
+
+Inherits from: ba.Setting
+A floating point game setting.
+
+Category: Settings Classes
+
+
+Methods:
+
+-
+
ba.FloatSetting(name: str, default: float, min_value: float = 0.0, max_value: float = 9999.0, increment: float = 1.0)
+
@@ -2317,7 +2389,7 @@ its time with lingering corpses, sound effects, etc.
Methods Inherited:
Methods Defined or Overridden:
-
+
-
ba.GameActivity(settings: dict)
@@ -2334,7 +2406,7 @@ and calls either end_game or continue_game depending on the result
-
<class method>
-create_settings_ui(sessionclass: Type[ba.Session], settings: Optional[Dict[str, Any]], completion_call: Callable[[Optional[Dict[str, Any]]], None]) -> None
+create_settings_ui(sessionclass: Type[ba.Session], settings: Optional[dict], completion_call: Callable[[Optional[dict]], None]) -> None
Launch an in-game UI to configure settings for a game type.
@@ -2343,11 +2415,11 @@ and calls either end_game or continue_game depending on the result
'config' should be an existing config dict (specifies 'edit' ui mode)
or None (specifies 'add' ui mode).
-'completion_call' will be called with a filled-out config dict on
+
'completion_call' will be called with a filled-out settings dict on
success or None on cancel.
Generally subclasses don't need to override this; if they override
-ba.GameActivity.get_game_settings() and
+ba.GameActivity.get_available_settings() and
ba.GameActivity.get_supported_maps() they can just rely on
the default implementation here which calls those methods.
@@ -2374,38 +2446,9 @@ there is no 'winner' yet; this way things like the standard time-limit
(ba.GameActivity.setup_standard_time_limit()) will work with the game.
--
+
-
<class method>
-get_description(sessiontype: Type[ba.Session]) -> str
-
-Get a str description of this game type.
-
-The default implementation simply returns the 'description' class var.
-Classes which want to change their description depending on the session
-can override this method.
-
-
--
-
<class method>
-get_description_display_string(sessiontype: Type[ba.Session]) -> ba.Lstr
-
-Return a translated version of get_description().
-
-Sub-classes should override get_description(); not this.
-
-
--
-
<class method>
-get_display_string(settings: Optional[Dict] = None) -> ba.Lstr
-
-Return a descriptive name for this game/settings combo.
-
-Subclasses should override getname(); not this.
-
-
--
-
<class method>
-get_game_settings(sessiontype: Type[ba.Session]) -> List[Tuple[str, Dict[str, Any]]]
+get_available_settings(sessiontype: Type[ba.Session]) -> List[ba.Setting]
Called by the default ba.GameActivity.create_settings_ui()
implementation; should return a dict of config options to be presented
@@ -2427,9 +2470,9 @@ of a name and a dict of options.
'increment': Value increment for int/float settings.
-# example get_game_settings() for a capture-the-flag game:
+# example get_available_settings() for a capture-the-flag game:
@classmethod
-def get_game_settings(cls, sessiontype):
+def get_available_settings(cls, sessiontype):
return [("Score to Win", {
'default': 3,
'min_value': 1
@@ -2463,6 +2506,35 @@ def get_game_settings(cls, sessiontype):
'default': False
})]
+
+-
+
<class method>
+get_description(sessiontype: Type[ba.Session]) -> str
+
+Get a str description of this game type.
+
+The default implementation simply returns the 'description' class var.
+Classes which want to change their description depending on the session
+can override this method.
+
+
+-
+
<class method>
+get_description_display_string(sessiontype: Type[ba.Session]) -> ba.Lstr
+
+Return a translated version of get_description().
+
+Sub-classes should override get_description(); not this.
+
+
+-
+
<class method>
+get_display_string(settings: Optional[Dict] = None) -> ba.Lstr
+
+Return a descriptive name for this game/settings combo.
+
+Subclasses should override getname(); not this.
+
-
get_instance_description(self) -> Union[str, Sequence]
@@ -2532,13 +2604,6 @@ with the first value, ${ARG2} with the second, etc.
This name is used above the game scoreboard in the corner
of the screen, so it should be as concise as possible.
-
--
-
<class method>
-get_score_info() -> ba.ScoreInfo
-
-Return info about game scoring setup; can be overridden by games.
-
-
<class method>
@@ -2574,6 +2639,13 @@ for this game-type for the given ba.Session type
This default implementation simply returns the 'name' class attr.
+
+-
+
<class method>
+getscoreconfig() -> ba.ScoreConfig
+
+Return info about game scoring setup; can be overridden by games.
+
-
handlemessage(self, msg: Any) -> Any
@@ -2842,6 +2914,36 @@ prefs, etc.
Methods:
<all methods inherited from ba.NotFoundError>
+Inherits from: ba.ChoiceSetting, ba.Setting
+An int setting with multiple choices.
+
+Category: Settings Classes
+
+
+Methods:
+
+-
+
ba.IntChoiceSetting(name: str, default: int, choices: List[Tuple[str, int]])
+
+
+
+
+Inherits from: ba.Setting
+An integer game setting.
+
+Category: Settings Classes
+
+
+Methods:
+
+-
+
ba.IntSetting(name: str, default: int, min_value: int = 0, max_value: int = 9999, increment: int = 1)
+
+
+
+
<top level class>
@@ -4345,38 +4447,38 @@ cause the powerup box to make a sound and disappear or whatnot.
-<top level class>
-Info about a game's scoring setup.
+Settings for how a game handles scores.
Category: Gameplay Classes
Attributes:
-label, lower_is_better, none_is_winner, scoretype, version
+label, lower_is_better, none_is_winner, scoretype, version
-label
-
+
label
-
str
A label show to the user for scores; 'Score', 'Time Survived', etc.
-lower_is_better
-
+
lower_is_better
-
bool
Whether lower scores are preferable. Higher scores are by default.
-none_is_winner
-
+
none_is_winner
-
bool
Whether a value of None is considered better than other scores.
By default it is not.
-scoretype
-
+
scoretype
-
ba.ScoreType
How the score value should be displayed.
-version
-
+
version
-
str
To change high-score lists used by a game without renaming the game,
change this. Defaults to an empty string.
@@ -4385,8 +4487,8 @@ change this. Defaults to an empty string.
Methods:
-<constructor>
-
-
ba.ScoreInfo(label: 'str' = 'Score', scoretype: 'ba.ScoreType' = <ScoreType.POINTS: 'p'>, lower_is_better: 'bool' = False, none_is_winner: 'bool' = False, version: 'str' = '')
+ <constructor>
-
+
ba.ScoreConfig(label: 'str' = 'Score', scoretype: 'ba.ScoreType' = <ScoreType.POINTS: 'p'>, lower_is_better: 'bool' = False, none_is_winner: 'bool' = False, version: 'str' = '')
@@ -4867,6 +4969,22 @@ of the session.
Methods:
<all methods inherited from ba.NotFoundError>
+ba.Setting
+
<top level class>
+
+Defines a user-controllable setting for a game or other entity.
+
+Category: Gameplay Classes
+
+
+Methods:
+
+<constructor>
-
+
ba.Setting(name: str, default: Any)
+
+
+
+
ba.ShouldShatterMessage
<top level class>
@@ -5230,7 +5348,7 @@ of the session.
Methods Inherited:
-add_actor_weak_ref(), add_player(), add_team(), begin(), continue_or_end_game(), create_player(), create_settings_ui(), create_team(), dep_is_present(), end_game(), expire(), get_description(), get_description_display_string(), get_display_string(), get_dynamic_deps(), get_game_settings(), get_instance_description(), get_instance_description_short(), get_instance_display_string(), get_instance_scoreboard_display_string(), get_score_info(), get_settings_display_string(), get_supported_maps(), get_team_display_string(), getname(), handlemessage(), has_begun(), has_ended(), has_transitioned_in(), is_transitioning_out(), is_waiting_for_continue(), on_continue(), on_expire(), on_player_join(), on_player_leave(), on_team_join(), on_team_leave(), on_transition_out(), remove_player(), remove_team(), respawn_player(), retain_actor(), set_has_ended(), setup_standard_powerup_drops(), setup_standard_time_limit(), show_zoom_message(), spawn_player(), spawn_player_if_exists(), transition_in(), transition_out()
+add_actor_weak_ref(), add_player(), add_team(), begin(), continue_or_end_game(), create_player(), create_settings_ui(), create_team(), dep_is_present(), end_game(), expire(), get_available_settings(), get_description(), get_description_display_string(), get_display_string(), get_dynamic_deps(), get_instance_description(), get_instance_description_short(), get_instance_display_string(), get_instance_scoreboard_display_string(), get_settings_display_string(), get_supported_maps(), get_team_display_string(), getname(), getscoreconfig(), handlemessage(), has_begun(), has_ended(), has_transitioned_in(), is_transitioning_out(), is_waiting_for_continue(), on_continue(), on_expire(), on_player_join(), on_player_leave(), on_team_join(), on_team_leave(), on_transition_out(), remove_player(), remove_team(), respawn_player(), retain_actor(), set_has_ended(), setup_standard_powerup_drops(), setup_standard_time_limit(), show_zoom_message(), spawn_player(), spawn_player_if_exists(), transition_in(), transition_out()
Methods Defined or Overridden:
<constructor>, end(), on_begin(), on_transition_in(), spawn_player_spaz(), supports_session_type()