More modernizing

This commit is contained in:
Eric Froemling 2020-05-25 21:20:16 -07:00
parent 808ea7dcdd
commit 06dfa73d01
29 changed files with 432 additions and 454 deletions

View File

@ -4132,16 +4132,16 @@
"assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c", "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/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", "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/e4/f0/c3261a8a7391a2b654da82c35c21", "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a1/97/fd0e5553917019236912a7010d38",
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/33/e2/85a8cbf23404612e6761cf02348b", "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/43/32/c48a4da7fcc8c17132077da852d7",
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5b/0d/d906be5fc2a75b5214a94cafe6ca", "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/75/13/2bc3ae9026386d1b515ede107e17",
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/6b/53/72d665caf5dfed84312ae6862642", "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/1f/9a/b112f788f528ec9e3627622698ac",
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/68/2d98f2d1ee67b6d54adbdf76c863", "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a1/87/fdb3e925f3be1cc353db94d61c5a",
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e5/3a/33f8f311260542126acea44d05e2", "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/fa/ba/125457c69bc2940850ab84c4cc6a",
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/18/eb/dd39bcfa33983864089d3fb7f666", "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a1/d4/5efcef1f85b2bfea7bbb81800580",
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f8/1d/3440010fcbcf18c5a6b2966f7fca", "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/fa/66/75a3a0f27d07131473e804a5b937",
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/9b/fa/b4ad4b8b82fefe1faad610065b9f", "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/51/6c/911ba80ba913e6dac1cafa5c8a74",
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/7f/b9/03af92db6140135ddfa467a1545d", "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/01/cc/cf896173a8d5731ada2aace59146",
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/69/b9/687fc5fa38c3faf7a47eb4241a3c", "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a2/12/aa1f0ca5506f0b57f93ca9adf7be",
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/cf/2b/589db924b489972981e10850a4e3" "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/e1/5f/f372d0ba9591b99284b9b50d7cdf"
} }

View File

@ -480,6 +480,7 @@
<w>downmix</w> <w>downmix</w>
<w>dpad</w> <w>dpad</w>
<w>dpath</w> <w>dpath</w>
<w>dprofilename</w>
<w>drawscore</w> <w>drawscore</w>
<w>drawscreen</w> <w>drawscreen</w>
<w>dripity</w> <w>dripity</w>
@ -756,8 +757,10 @@
<w>getcurrency</w> <w>getcurrency</w>
<w>getcwd</w> <w>getcwd</w>
<w>getdata</w> <w>getdata</w>
<w>getinputdevice</w>
<w>getkillerplayer</w> <w>getkillerplayer</w>
<w>getlevelname</w> <w>getlevelname</w>
<w>getlog</w>
<w>getmaps</w> <w>getmaps</w>
<w>getmodel</w> <w>getmodel</w>
<w>getnodes</w> <w>getnodes</w>
@ -937,6 +940,7 @@
<w>joedeshon</w> <w>joedeshon</w>
<w>johab</w> <w>johab</w>
<w>joinable</w> <w>joinable</w>
<w>joinmsg</w>
<w>jovica</w> <w>jovica</w>
<w>jsonstr</w> <w>jsonstr</w>
<w>jsonstrbase</w> <w>jsonstrbase</w>
@ -1267,6 +1271,8 @@
<w>openssh</w> <w>openssh</w>
<w>operasinger</w> <w>operasinger</w>
<w>oppnode</w> <w>oppnode</w>
<w>opposingbody</w>
<w>opposingnode</w>
<w>opstr</w> <w>opstr</w>
<w>optparse</w> <w>optparse</w>
<w>orchestrahitsound</w> <w>orchestrahitsound</w>
@ -1504,6 +1510,7 @@
<w>qrencode</w> <w>qrencode</w>
<w>qual</w> <w>qual</w>
<w>quoprimime</w> <w>quoprimime</w>
<w>rando</w>
<w>randommodule</w> <w>randommodule</w>
<w>randval</w> <w>randval</w>
<w>rankbutton</w> <w>rankbutton</w>
@ -1672,6 +1679,7 @@
<w>soundtrackname</w> <w>soundtrackname</w>
<w>sourceimages</w> <w>sourceimages</w>
<w>sourcelines</w> <w>sourcelines</w>
<w>sourcenode</w>
<w>spacelen</w> <w>spacelen</w>
<w>spacingstr</w> <w>spacingstr</w>
<w>spammers</w> <w>spammers</w>

View File

@ -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) # (hash we can use to see if this file is out of date)
# SOURCES_HASH=164420280597992494471294420110866243586 # SOURCES_HASH=318889180473540875493180206824864665481
# I'm sorry Pylint. I know this file saddens you. Be strong. # I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression # pylint: disable=useless-suppression
@ -691,8 +691,8 @@ class Node:
def delete(self, ignore_missing: bool = True) -> None: def delete(self, ignore_missing: bool = True) -> None:
"""delete(ignore_missing: bool = True) -> None """delete(ignore_missing: bool = True) -> None
Delete the node. Ignores already-deleted nodes unless ignore_missing Delete the node. Ignores already-deleted nodes if ignore_missing
is False, in which case an Exception is thrown. is True; otherwise a ba.NodeNotFoundError is thrown.
""" """
return None return None
@ -716,6 +716,7 @@ class Node:
""" """
return str() return str()
# Show that ur return type varies based on "doraise" value:
@overload @overload
def getdelegate(self, def getdelegate(self,
type: Type[_T], type: Type[_T],
@ -733,7 +734,7 @@ class Node:
If the node has no delegate or it is not an instance of the passed If the node has no delegate or it is not an instance of the passed
type, then None will be returned. If 'doraise' is True, then an type, then None will be returned. If 'doraise' is True, then an
Exception will be raised instead in such cases. ba.DelegateNotFoundError will be raised instead.
""" """
return None return None
@ -818,6 +819,9 @@ class SessionPlayer:
storing data associated with this Player. storing data associated with this Player.
This gets cleared for each new ba.Activity. This gets cleared for each new ba.Activity.
inputdevice: ba.InputDevice
The input device associated with the player.
color: Sequence[float] color: Sequence[float]
The base color for this Player. The base color for this Player.
In team games this will match the ba.SessionTeam's color. In team games this will match the ba.SessionTeam's color.
@ -839,6 +843,7 @@ class SessionPlayer:
team: ba.SessionTeam team: ba.SessionTeam
sessiondata: Dict sessiondata: Dict
gamedata: Dict gamedata: Dict
inputdevice: ba.InputDevice
color: Sequence[float] color: Sequence[float]
highlight: Sequence[float] highlight: Sequence[float]
character: str character: str
@ -892,14 +897,6 @@ class SessionPlayer:
""" """
return {'foo': 'bar'} return {'foo': 'bar'}
def get_input_device(self) -> ba.InputDevice:
"""get_input_device() -> ba.InputDevice
Returns the player's input device.
"""
import ba # pylint: disable=cyclic-import
return ba.InputDevice()
def get_name(self, full: bool = False, icon: bool = True) -> str: def get_name(self, full: bool = False, icon: bool = True) -> str:
"""get_name(full: bool = False, icon: bool = True) -> str """get_name(full: bool = False, icon: bool = True) -> str
@ -2065,22 +2062,6 @@ def get_idle_time() -> int:
return int() return int()
def get_input_device(name: str,
unique_id: str,
doraise: bool = True) -> ba.InputDevice:
"""get_input_device(name: str, unique_id: str, doraise: bool = True)
-> ba.InputDevice
(internal)
Given a type name and a unique identifier, returns an InputDevice.
Throws an Exception if the input-device is not found, or returns None
if 'doraise' is False.
"""
import ba # pylint: disable=cyclic-import
return ba.InputDevice()
def get_local_active_input_devices_count() -> int: def get_local_active_input_devices_count() -> int:
"""get_local_active_input_devices_count() -> int """get_local_active_input_devices_count() -> int
@ -2089,14 +2070,6 @@ def get_local_active_input_devices_count() -> int:
return int() return int()
def get_log() -> str:
"""get_log() -> str
(internal)
"""
return str()
def get_log_file_path() -> str: def get_log_file_path() -> str:
"""get_log_file_path() -> str """get_log_file_path() -> str
@ -2389,6 +2362,41 @@ def getdata(name: str) -> ba.Data:
return ba.Data() return ba.Data()
# Show that our return type varies based on "doraise" value:
@overload
def getinputdevice(name: str,
unique_id: str,
doraise: Literal[True] = True) -> ba.InputDevice:
...
@overload
def getinputdevice(name: str, unique_id: str,
doraise: Literal[False]) -> Optional[ba.InputDevice]:
...
def getinputdevice(name: str, unique_id: str, doraise: bool = True) -> Any:
"""getinputdevice(name: str, unique_id: str, doraise: bool = True)
-> <varies>
(internal)
Given a type name and a unique identifier, returns an InputDevice.
Throws an Exception if the input-device is not found, or returns None
if 'doraise' is False.
"""
return None
def getlog() -> str:
"""getlog() -> str
(internal)
"""
return str()
def getmodel(name: str) -> ba.Model: def getmodel(name: str) -> ba.Model:
"""getmodel(name: str) -> ba.Model """getmodel(name: str) -> ba.Model

View File

@ -53,7 +53,7 @@ from ba._error import (print_exception, print_error, NotFoundError,
InputDeviceNotFoundError, WidgetNotFoundError, InputDeviceNotFoundError, WidgetNotFoundError,
ActivityNotFoundError, TeamNotFoundError, ActivityNotFoundError, TeamNotFoundError,
SessionTeamNotFoundError, SessionNotFoundError, SessionTeamNotFoundError, SessionNotFoundError,
DependencyError) DelegateNotFoundError, DependencyError)
from ba._freeforallsession import FreeForAllSession from ba._freeforallsession import FreeForAllSession
from ba._gameactivity import GameActivity from ba._gameactivity import GameActivity
from ba._gameresults import TeamGameResults from ba._gameresults import TeamGameResults

View File

@ -22,6 +22,7 @@
from __future__ import annotations from __future__ import annotations
import time import time
import random
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import _ba import _ba
@ -376,7 +377,7 @@ class App:
# Lobby. # Lobby.
self.lobby_random_profile_index: int = 1 self.lobby_random_profile_index: int = 1
self.lobby_random_char_index_offset: Optional[int] = None self.lobby_random_char_index_offset = random.randrange(1000)
self.lobby_account_profile_device_id: Optional[int] = None self.lobby_account_profile_device_id: Optional[int] = None
# Main Menu. # Main Menu.
@ -722,7 +723,7 @@ class App:
game: str, game: str,
force: bool = False, force: bool = False,
args: Dict = None) -> bool: args: Dict = None) -> bool:
"""High level way to launch a co-op session locally.""" """High level way to launch a local co-op session."""
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
from ba._campaign import get_campaign from ba._campaign import get_campaign
from bastd.ui.coop.level import CoopLevelLockedWindow from bastd.ui.coop.level import CoopLevelLockedWindow
@ -788,7 +789,7 @@ class App:
color=(1, 1, 0)), color=(1, 1, 0)),
timetype=TimeType.REAL) timetype=TimeType.REAL)
def shutdown(self) -> None: def on_app_shutdown(self) -> None:
"""(internal)""" """(internal)"""
self.music.on_app_shutdown() self.music.on_app_shutdown()

View File

@ -43,7 +43,7 @@ def is_browser_likely_available() -> bool:
""" """
app = _ba.app app = _ba.app
platform = app.platform platform = app.platform
touchscreen = _ba.get_input_device('TouchScreen', '#1', doraise=False) touchscreen = _ba.getinputdevice('TouchScreen', '#1', doraise=False)
# If we're on a vr device or an android device with no touchscreen, # If we're on a vr device or an android device with no touchscreen,
# assume no browser. # assume no browser.
@ -100,7 +100,7 @@ def handle_log() -> None:
except Exception: except Exception:
activityname = 'unavailable' activityname = 'unavailable'
info = { info = {
'log': _ba.get_log(), 'log': _ba.getlog(),
'version': app.version, 'version': app.version,
'build': app.build_number, 'build': app.build_number,
'userAgentString': app.user_agent_string, 'userAgentString': app.user_agent_string,

View File

@ -40,37 +40,37 @@ class Collision:
return _ba.Vec3(_ba.get_collision_info('position')) return _ba.Vec3(_ba.get_collision_info('position'))
@property @property
def source_node(self) -> ba.Node: def sourcenode(self) -> ba.Node:
"""The node containing the material triggering the current callback. """The node containing the material triggering the current callback.
Throws a ba.NodeNotFoundError if the node does not exist, though Throws a ba.NodeNotFoundError if the node does not exist, though
the node should always exist (at least at the start of the collision the node should always exist (at least at the start of the collision
callback). callback).
""" """
node = _ba.get_collision_info('source_node') node = _ba.get_collision_info('sourcenode')
assert isinstance(node, (_ba.Node, type(None))) assert isinstance(node, (_ba.Node, type(None)))
if not node: if not node:
raise NodeNotFoundError() raise NodeNotFoundError()
return node return node
@property @property
def opposing_node(self) -> ba.Node: def opposingnode(self) -> ba.Node:
"""The node the current callback material node is hitting. """The node the current callback material node is hitting.
Throws a ba.NodeNotFoundError if the node does not exist. Throws a ba.NodeNotFoundError if the node does not exist.
This can be expected in some cases such as in 'disconnect' This can be expected in some cases such as in 'disconnect'
callbacks triggered by deleting a currently-colliding node. callbacks triggered by deleting a currently-colliding node.
""" """
node = _ba.get_collision_info('opposing_node') node = _ba.get_collision_info('opposingnode')
assert isinstance(node, (_ba.Node, type(None))) assert isinstance(node, (_ba.Node, type(None)))
if not node: if not node:
raise NodeNotFoundError() raise NodeNotFoundError()
return node return node
@property @property
def opposing_body(self) -> int: def opposingbody(self) -> int:
"""The body index on the opposing node in the current collision.""" """The body index on the opposing node in the current collision."""
body = _ba.get_collision_info('opposing_body') body = _ba.get_collision_info('opposingbody')
assert isinstance(body, int) assert isinstance(body, int)
return body return body

View File

@ -77,6 +77,13 @@ class TeamNotFoundError(NotFoundError):
""" """
class DelegateNotFoundError(NotFoundError):
"""Exception raised when an expected delegate object does not exist.
category: Exception Classes
"""
class SessionTeamNotFoundError(NotFoundError): class SessionTeamNotFoundError(NotFoundError):
"""Exception raised when an expected ba.SessionTeam does not exist. """Exception raised when an expected ba.SessionTeam does not exist.

View File

@ -194,10 +194,10 @@ def animate(node: ba.Node,
# Temp sanity check while we transition from milliseconds to seconds # Temp sanity check while we transition from milliseconds to seconds
# based time values. # based time values.
if _ba.app.test_build and not suppress_format_warning: if __debug__:
for item in items: if not suppress_format_warning:
# (PyCharm seems to think item is a float, not a tuple) for item in items:
_ba.time_format_check(timeformat, item[0]) _ba.time_format_check(timeformat, item[0])
curve = _ba.newnode('animcurve', curve = _ba.newnode('animcurve',
owner=node, owner=node,
@ -221,7 +221,6 @@ def animate(node: ba.Node,
# FIXME: Even if we are looping we should have a way to die once we # FIXME: Even if we are looping we should have a way to die once we
# get disconnected. # get disconnected.
if not loop: if not loop:
# (PyCharm seems to think item is a float, not a tuple)
_ba.timer(int(mult * items[-1][0]) + 1000, _ba.timer(int(mult * items[-1][0]) + 1000,
curve.delete, curve.delete,
timeformat=TimeFormat.MILLISECONDS) timeformat=TimeFormat.MILLISECONDS)
@ -259,10 +258,11 @@ def animate_array(node: ba.Node,
# Temp sanity check while we transition from milliseconds to seconds # Temp sanity check while we transition from milliseconds to seconds
# based time values. # based time values.
if _ba.app.test_build and not suppress_format_warning: if __debug__:
for item in items: if not suppress_format_warning:
# (PyCharm seems to think item is a float, not a tuple) for item in items:
_ba.time_format_check(timeformat, item[0]) # (PyCharm seems to think item is a float, not a tuple)
_ba.time_format_check(timeformat, item[0])
if timeformat is TimeFormat.SECONDS: if timeformat is TimeFormat.SECONDS:
mult = 1000 mult = 1000
@ -381,8 +381,9 @@ def timestring(timeval: float,
# Temp sanity check while we transition from milliseconds to seconds # Temp sanity check while we transition from milliseconds to seconds
# based time values. # based time values.
if _ba.app.test_build and not suppress_format_warning: if __debug__:
_ba.time_format_check(timeformat, timeval) if not suppress_format_warning:
_ba.time_format_check(timeformat, timeval)
# We operate on milliseconds internally. # We operate on milliseconds internally.
if timeformat is TimeFormat.SECONDS: if timeformat is TimeFormat.SECONDS:

View File

@ -293,7 +293,7 @@ def do_quit() -> None:
def shutdown() -> None: def shutdown() -> None:
_ba.app.shutdown() _ba.app.on_app_shutdown()
def gc_disable() -> None: def gc_disable() -> None:

View File

@ -19,16 +19,19 @@
# SOFTWARE. # SOFTWARE.
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
"""Implements lobby system for gathering before games, char select, etc.""" """Implements lobby system for gathering before games, char select, etc."""
# pylint: disable=too-many-lines
from __future__ import annotations from __future__ import annotations
import random
import weakref import weakref
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import _ba import _ba
from ba._error import print_exception, print_error, NotFoundError
from ba._gameutils import animate, animate_array
from ba._lang import Lstr
from ba._enums import SpecialChar
from ba._profile import get_player_profile_colors
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Optional, List, Dict, Any, Sequence, Union from typing import Optional, List, Dict, Any, Sequence, Union
@ -44,45 +47,22 @@ class JoinInfo:
"""Display useful info for joiners.""" """Display useful info for joiners."""
def __init__(self, lobby: ba.Lobby): def __init__(self, lobby: ba.Lobby):
# pylint: disable=too-many-locals
from ba import _input
from ba._lang import Lstr
from ba._nodeactor import NodeActor from ba._nodeactor import NodeActor
from ba._general import WeakCall from ba._general import WeakCall
from ba._enums import SpecialChar
can_switch_teams = (len(lobby.teams) > 1)
self._state = 0 self._state = 0
press_to_punch: Union[str, self._press_to_punch: Union[str, ba.Lstr] = _ba.charstr(
ba.Lstr] = _ba.charstr(SpecialChar.LEFT_BUTTON) SpecialChar.LEFT_BUTTON)
press_to_bomb: Union[str, self._press_to_bomb: Union[str, ba.Lstr] = _ba.charstr(
ba.Lstr] = _ba.charstr(SpecialChar.RIGHT_BUTTON) SpecialChar.RIGHT_BUTTON)
self._joinmsg = Lstr(resource='pressAnyButtonToJoinText')
can_switch_teams = (len(lobby.teams) > 1)
# If we have a keyboard, grab keys for punch and pickup. # If we have a keyboard, grab keys for punch and pickup.
# FIXME: This of course is only correct on the local device; # FIXME: This of course is only correct on the local device;
# Should change this for net games. # Should change this for net games.
keyboard: Optional[ba.InputDevice] = _ba.get_input_device( keyboard = _ba.getinputdevice('Keyboard', '#1', doraise=False)
'Keyboard', '#1', doraise=False)
if keyboard is not None: if keyboard is not None:
punch_key = keyboard.get_button_name( self._update_for_keyboard(keyboard)
_input.get_device_value(keyboard, 'buttonPunch'))
press_to_punch = Lstr(resource='orText',
subs=[('${A}',
Lstr(value='\'${K}\'',
subs=[('${K}', punch_key)])),
('${B}', press_to_punch)])
bomb_key = keyboard.get_button_name(
_input.get_device_value(keyboard, 'buttonBomb'))
press_to_bomb = Lstr(resource='orText',
subs=[('${A}',
Lstr(value='\'${K}\'',
subs=[('${K}', bomb_key)])),
('${B}', press_to_bomb)])
join_str = Lstr(value='${A} < ${B} >',
subs=[('${A}',
Lstr(resource='pressPunchToJoinText')),
('${B}', press_to_punch)])
else:
join_str = Lstr(resource='pressAnyButtonToJoinText')
flatness = 1.0 if _ba.app.vr_mode else 0.0 flatness = 1.0 if _ba.app.vr_mode else 0.0
self._text = NodeActor( self._text = NodeActor(
@ -94,11 +74,11 @@ class JoinInfo:
'h_align': 'center', 'h_align': 'center',
'color': (0.7, 0.7, 0.95, 1.0), 'color': (0.7, 0.7, 0.95, 1.0),
'flatness': flatness, 'flatness': flatness,
'text': join_str 'text': self._joinmsg
})) }))
if _ba.app.kiosk_mode: if _ba.app.kiosk_mode:
self._messages = [join_str] self._messages = [self._joinmsg]
else: else:
msg1 = Lstr(resource='pressToSelectProfileText', msg1 = Lstr(resource='pressToSelectProfileText',
subs=[ subs=[
@ -108,15 +88,38 @@ class JoinInfo:
msg2 = Lstr(resource='pressToOverrideCharacterText', msg2 = Lstr(resource='pressToOverrideCharacterText',
subs=[('${BUTTONS}', Lstr(resource='bombBoldText'))]) subs=[('${BUTTONS}', Lstr(resource='bombBoldText'))])
msg3 = Lstr(value='${A} < ${B} >', msg3 = Lstr(value='${A} < ${B} >',
subs=[('${A}', msg2), ('${B}', press_to_bomb)]) subs=[('${A}', msg2), ('${B}', self._press_to_bomb)])
self._messages = (([ self._messages = (([
Lstr(resource='pressToSelectTeamText', Lstr(
subs=[('${BUTTONS}', _ba.charstr(SpecialChar.LEFT_ARROW) + resource='pressToSelectTeamText',
' ' + _ba.charstr(SpecialChar.RIGHT_ARROW))]) subs=[('${BUTTONS}', _ba.charstr(SpecialChar.LEFT_ARROW) +
] if can_switch_teams else []) + [msg1] + [msg3] + [join_str]) ' ' + _ba.charstr(SpecialChar.RIGHT_ARROW))],
)
] if can_switch_teams else []) + [msg1] + [msg3] + [self._joinmsg])
self._timer = _ba.Timer(4.0, WeakCall(self._update), repeat=True) self._timer = _ba.Timer(4.0, WeakCall(self._update), repeat=True)
def _update_for_keyboard(self, keyboard: ba.InputDevice) -> None:
from ba import _input
punch_key = keyboard.get_button_name(
_input.get_device_value(keyboard, 'buttonPunch'))
self._press_to_punch = Lstr(resource='orText',
subs=[('${A}',
Lstr(value='\'${K}\'',
subs=[('${K}', punch_key)])),
('${B}', self._press_to_punch)])
bomb_key = keyboard.get_button_name(
_input.get_device_value(keyboard, 'buttonBomb'))
self._press_to_bomb = Lstr(resource='orText',
subs=[('${A}',
Lstr(value='\'${K}\'',
subs=[('${K}', bomb_key)])),
('${B}', self._press_to_bomb)])
self._joinmsg = Lstr(value='${A} < ${B} >',
subs=[('${A}',
Lstr(resource='pressPunchToJoinText')),
('${B}', self._press_to_punch)])
def _update(self) -> None: def _update(self) -> None:
assert self._text.node assert self._text.node
self._text.node.text = self._messages[self._state] self._text.node.text = self._messages[self._state]
@ -150,13 +153,6 @@ class Chooser:
def __init__(self, vpos: float, player: _ba.SessionPlayer, def __init__(self, vpos: float, player: _ba.SessionPlayer,
lobby: 'Lobby') -> None: lobby: 'Lobby') -> None:
# FIXME: Tidy up around here.
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
from ba import _gameutils
from ba import _profile
from ba import _lang
app = _ba.app
self._deek_sound = _ba.getsound('deek') self._deek_sound = _ba.getsound('deek')
self._click_sound = _ba.getsound('click01') self._click_sound = _ba.getsound('click01')
self._punchsound = _ba.getsound('punch01') self._punchsound = _ba.getsound('punch01')
@ -172,11 +168,16 @@ class Chooser:
self._profilename = '' self._profilename = ''
self._profilenames: List[str] = [] self._profilenames: List[str] = []
self._ready: bool = False self._ready: bool = False
self.character_names: List[str] = [] self._character_names: List[str] = []
self.last_change: Sequence[Union[float, int]] = (0, 0) self._last_change: Sequence[Union[float, int]] = (0, 0)
self._profiles: Dict[str, Dict[str, Any]] = {}
# Hmm does this need to be public? app = _ba.app
self.profiles: Dict[str, Dict[str, Any]] = {}
# try:
# print(player.inputdevice)
# except Exception as exc:
# print('GOT EXC', type(exc))
# Load available profiles either from the local config or from the # Load available profiles either from the local config or from the
# remote device. # remote device.
@ -186,109 +187,26 @@ class Chooser:
# the team-id! # the team-id!
self._selected_team_index: int = self.lobby.next_add_team self._selected_team_index: int = self.lobby.next_add_team
# Store a persistent random character index; we'll use this for the # Store a persistent random character index and colors; we'll use this
# '_random' profile. Let's use their input_device id to seed it. This # for the '_random' profile. Let's use their input_device id to seed
# will give a persistent character for them between games and will # it. This will give a persistent character for them between games
# distribute characters nicely if everyone is random. # and will distribute characters nicely if everyone is random.
try: self._random_color, self._random_highlight = (
input_device_id = self._player.get_input_device().id get_player_profile_colors(None))
except Exception:
from ba import _error
_error.print_exception('Error getting device-id on chooser create')
input_device_id = 0
if app.lobby_random_char_index_offset is None: # To calc our random character we pick a random one out of our
# We want the first device that asks for a chooser to always get
# spaz as a random character..
# scratch that.. we now kinda accomplish the same thing with
# account profiles so lets just be fully random here.
app.lobby_random_char_index_offset = (random.randrange(1000))
# To calc our random index we pick a random character out of our
# unlocked list and then locate that character's index in the full # unlocked list and then locate that character's index in the full
# list. # list.
char_index_offset = app.lobby_random_char_index_offset char_index_offset = app.lobby_random_char_index_offset
assert char_index_offset is not None self._random_character_index = (
self._random_character_index = ((input_device_id + char_index_offset) % (player.inputdevice.id + char_index_offset) %
len(self.character_names)) len(self._character_names))
self._random_color, self._random_highlight = (
_profile.get_player_profile_colors(None))
# Attempt to pick an initial profile based on what's been stored # Attempt to set an initial profile based on what was used previously
# for this input device. # for this input-device, etc.
input_device = self._player.get_input_device() self._profileindex = self._select_initial_profile()
try: self._profilename = self._profilenames[self._profileindex]
name = input_device.name
unique_id = input_device.unique_identifier
self._profilename = (
app.config['Default Player Profiles'][name + ' ' + unique_id])
self._profileindex = self._profilenames.index(self._profilename)
# If this one is __account__ and is local and we haven't marked
# anyone as the account-profile device yet, mark this guy as it.
# (prevents the next joiner from getting the account profile too).
if (self._profilename == '__account__'
and not input_device.is_remote_client
and app.lobby_account_profile_device_id is None):
app.lobby_account_profile_device_id = input_device_id
# Well hmm that didn't work.. pick __account__, _random, or some
# other random profile.
except Exception:
profilenames = self._profilenames
# We want the first local input-device in the game to latch on to
# the account profile.
if (not input_device.is_remote_client
and not input_device.is_controller_app):
if (app.lobby_account_profile_device_id is None
and '__account__' in profilenames):
app.lobby_account_profile_device_id = input_device_id
# If this is the designated account-profile-device, try to default
# to the account profile.
if (input_device_id == app.lobby_account_profile_device_id
and '__account__' in profilenames):
self._profileindex = profilenames.index('__account__')
else:
# If this is the controller app, it defaults to using a random
# profile (since we can pull the random name from the app).
if input_device.is_controller_app:
self._profileindex = profilenames.index('_random')
else:
# If its a client connection, for now just force
# the account profile if possible.. (need to provide a
# way for clients to specify/remember their default
# profile on remote servers that do not already know them).
if (input_device.is_remote_client
and '__account__' in profilenames):
self._profileindex = profilenames.index('__account__')
else:
# Cycle through our non-random profiles once; after
# that, everyone gets random.
while (app.lobby_random_profile_index <
len(profilenames)
and profilenames[app.lobby_random_profile_index]
in ('_random', '__account__', '_edit')):
app.lobby_random_profile_index += 1
if (app.lobby_random_profile_index <
len(profilenames)):
self._profileindex = (
app.lobby_random_profile_index)
app.lobby_random_profile_index += 1
else:
self._profileindex = profilenames.index('_random')
self._profilename = profilenames[self._profileindex]
self.character_index = self._random_character_index
self._color = self._random_color
self._highlight = self._random_highlight
self._text_node = _ba.newnode('text', self._text_node = _ba.newnode('text',
delegate=self, delegate=self,
attrs={ attrs={
@ -300,8 +218,7 @@ class Chooser:
'v_align': 'center', 'v_align': 'center',
'v_attach': 'top' 'v_attach': 'top'
}) })
animate(self._text_node, 'scale', {0: 0, 0.1: 1.0})
_gameutils.animate(self._text_node, 'scale', {0: 0, 0.1: 1.0})
self.icon = _ba.newnode('image', self.icon = _ba.newnode('image',
owner=self._text_node, owner=self._text_node,
attrs={ attrs={
@ -311,20 +228,83 @@ class Chooser:
'attach': 'topCenter' 'attach': 'topCenter'
}) })
_gameutils.animate_array(self.icon, 'scale', 2, { animate_array(self.icon, 'scale', 2, {0: (0, 0), 0.1: (45, 45)})
0: (0, 0),
0.1: (45, 45) # Set our initial name to '<choosing player>' in case anyone asks.
}) self._player.set_name(Lstr(resource='choosingPlayerText').evaluate(),
real=False)
# Init these to our rando but they should get switched to the
# selected profile (if any) right after.
self._character_index = self._random_character_index
self._color = self._random_color
self._highlight = self._random_highlight
self.update_from_profile()
self.update_position()
self._inited = True
self._set_ready(False) self._set_ready(False)
# Set our initial name to '<choosing player>' in case anyone asks. def _select_initial_profile(self) -> int:
self._player.set_name( app = _ba.app
_lang.Lstr(resource='choosingPlayerText').evaluate(), real=False) profilenames = self._profilenames
inputdevice = self._player.inputdevice
self.update_from_player_profiles() # If we've got a set profile name for this device, work backwards
self.update_position() # from that to get our index.
self._inited = True dprofilename = (app.config.get('Default Player Profiles',
{}).get(inputdevice.name + ' ' +
inputdevice.unique_identifier))
if dprofilename is not None and dprofilename in profilenames:
# If we got '__account__' and its local and we haven't marked
# anyone as the 'account profile' device yet, mark this guy as
# it. (prevents the next joiner from getting the account
# profile too).
if (dprofilename == '__account__'
and not inputdevice.is_remote_client
and app.lobby_account_profile_device_id is None):
app.lobby_account_profile_device_id = inputdevice.id
return profilenames.index(dprofilename)
# We want to mark the first local input-device in the game
# as the 'account profile' device.
if (not inputdevice.is_remote_client
and not inputdevice.is_controller_app):
if (app.lobby_account_profile_device_id is None
and '__account__' in profilenames):
app.lobby_account_profile_device_id = inputdevice.id
# If this is the designated account-profile-device, try to default
# to the account profile.
if (inputdevice.id == app.lobby_account_profile_device_id
and '__account__' in profilenames):
return profilenames.index('__account__')
# If this is the controller app, it defaults to using a random
# profile (since we can pull the random name from the app).
if inputdevice.is_controller_app and '_random' in profilenames:
return profilenames.index('_random')
# If its a client connection, for now just force
# the account profile if possible.. (need to provide a
# way for clients to specify/remember their default
# profile on remote servers that do not already know them).
if inputdevice.is_remote_client and '__account__' in profilenames:
return profilenames.index('__account__')
# Cycle through our non-random profiles once; after
# that, everyone gets random.
while (app.lobby_random_profile_index < len(profilenames)
and profilenames[app.lobby_random_profile_index]
in ('_random', '__account__', '_edit')):
app.lobby_random_profile_index += 1
if app.lobby_random_profile_index < len(profilenames):
profileindex = app.lobby_random_profile_index
app.lobby_random_profile_index += 1
return profileindex
assert '_random' in profilenames
return profilenames.index('_random')
@property @property
def player(self) -> ba.SessionPlayer: def player(self) -> ba.SessionPlayer:
@ -353,44 +333,39 @@ class Chooser:
"""The chooser's ba.Lobby.""" """The chooser's ba.Lobby."""
lobby = self._lobby() lobby = self._lobby()
if lobby is None: if lobby is None:
from ba import _error raise NotFoundError('Lobby does not exist.')
raise _error.NotFoundError('Lobby does not exist.')
return lobby return lobby
def get_lobby(self) -> Optional[ba.Lobby]: def get_lobby(self) -> Optional[ba.Lobby]:
"""Return this chooser's lobby if it still exists; otherwise None.""" """Return this chooser's lobby if it still exists; otherwise None."""
return self._lobby() return self._lobby()
def update_from_player_profiles(self) -> None: def update_from_profile(self) -> None:
"""Set character based on profile; otherwise use pre-picked random.""" """Set character/colors based on the current profile."""
try: self._profilename = self._profilenames[self._profileindex]
from ba import _profile if self._profilename == '_edit':
pass
# Store the name even though we usually use index (in case elif self._profilename == '_random':
# the profile list changes) self._character_index = self._random_character_index
self._profilename = self._profilenames[self._profileindex] self._color = self._random_color
character = self.profiles[self._profilename]['character'] self._highlight = self._random_highlight
else:
character = self._profiles[self._profilename]['character']
# At the moment we're not properly pulling the list # At the moment we're not properly pulling the list
# of available characters from clients, so profiles might use a # of available characters from clients, so profiles might use a
# character not in their list. For now, just go ahead and add # character not in their list. For now, just go ahead and add
# a character name to their list as long as we're aware of it. # a character name to their list as long as we're aware of it.
# This just means they won't always be able to override their # This just means they won't always be able to override their
# character to others they own, but profile characters should work # character to others they own, but profile characters
# (and we validate profiles on the master server so no exploit # should work (and we validate profiles on the master server
# opportunities) # so no exploit opportunities)
if (character not in self.character_names if (character not in self._character_names
and character in _ba.app.spaz_appearances): and character in _ba.app.spaz_appearances):
self.character_names.append(character) self._character_names.append(character)
self.character_index = self.character_names.index(character) self._character_index = self._character_names.index(character)
self._color, self._highlight = (_profile.get_player_profile_colors( self._color, self._highlight = (get_player_profile_colors(
self._profilename, profiles=self.profiles)) self._profilename, profiles=self._profiles))
except Exception:
# FIXME: Should never use top level Exception for logic; only
# error catching (and they should always be logged).
self.character_index = self._random_character_index
self._color = self._random_color
self._highlight = self._random_highlight
self._update_icon() self._update_icon()
self._update_text() self._update_text()
@ -401,52 +376,51 @@ class Chooser:
# Re-construct our profile index and other stuff since the profile # Re-construct our profile index and other stuff since the profile
# list might have changed. # list might have changed.
input_device = self._player.get_input_device() input_device = self._player.inputdevice
is_remote = input_device.is_remote_client is_remote = input_device.is_remote_client
is_test_input = input_device.name.startswith('TestInput') is_test_input = input_device.name.startswith('TestInput')
# Pull this player's list of unlocked characters. # Pull this player's list of unlocked characters.
if is_remote: if is_remote:
# FIXME: Pull this from remote player (but make sure to # TODO: Pull this from the remote player.
# filter it to ones we've got). # (but make sure to filter it to the ones we've got).
self.character_names = ['Spaz'] self._character_names = ['Spaz']
else: else:
self.character_names = self.lobby.character_names_local_unlocked self._character_names = self.lobby.character_names_local_unlocked
# If we're a local player, pull our local profiles from the config. # If we're a local player, pull our local profiles from the config.
# Otherwise ask the remote-input-device for its profile list. # Otherwise ask the remote-input-device for its profile list.
if is_remote: if is_remote:
self.profiles = input_device.get_player_profiles() self._profiles = input_device.get_player_profiles()
else: else:
self.profiles = app.config.get('Player Profiles', {}) self._profiles = app.config.get('Player Profiles', {})
# These may have come over the wire from an older # These may have come over the wire from an older
# (non-unicode/non-json) version. # (non-unicode/non-json) version.
# Make sure they conform to our standards # Make sure they conform to our standards
# (unicode strings, no tuples, etc) # (unicode strings, no tuples, etc)
self.profiles = json_prep(self.profiles) self._profiles = json_prep(self._profiles)
# Filter out any characters we're unaware of. # Filter out any characters we're unaware of.
for profile in list(self.profiles.items()): for profile in list(self._profiles.items()):
if profile[1].get('character', '') not in app.spaz_appearances: if profile[1].get('character', '') not in app.spaz_appearances:
profile[1]['character'] = 'Spaz' profile[1]['character'] = 'Spaz'
# Add in a random one so we're ok even if there's no # Add in a random one so we're ok even if there's no user profiles.
# user-created profiles. self._profiles['_random'] = {}
self.profiles['_random'] = {}
# In kiosk mode we disable account profiles to force random. # In kiosk mode we disable account profiles to force random.
if app.kiosk_mode: if app.kiosk_mode:
if '__account__' in self.profiles: if '__account__' in self._profiles:
del self.profiles['__account__'] del self._profiles['__account__']
# For local devices, add it an 'edit' option which will pop up # For local devices, add it an 'edit' option which will pop up
# the profile window. # the profile window.
if not is_remote and not is_test_input and not app.kiosk_mode: if not is_remote and not is_test_input and not app.kiosk_mode:
self.profiles['_edit'] = {} self._profiles['_edit'] = {}
# Build a sorted name list we can iterate through. # Build a sorted name list we can iterate through.
self._profilenames = list(self.profiles.keys()) self._profilenames = list(self._profiles.keys())
self._profilenames.sort(key=lambda x: x.lower()) self._profilenames.sort(key=lambda x: x.lower())
if self._profilename in self._profilenames: if self._profilename in self._profilenames:
@ -457,90 +431,66 @@ class Chooser:
def update_position(self) -> None: def update_position(self) -> None:
"""Update this chooser's position.""" """Update this chooser's position."""
from ba import _gameutils
# Hmmm this shouldn't be happening. assert self._text_node
if not self._text_node:
print('Error: chooser text nonexistent..')
import traceback
traceback.print_stack()
return
spacing = 350 spacing = 350
teams = self.lobby.teams teams = self.lobby.teams
offs = (spacing * -0.5 * len(teams) + offs = (spacing * -0.5 * len(teams) +
spacing * self._selected_team_index + 250) spacing * self._selected_team_index + 250)
if len(teams) > 1: if len(teams) > 1:
offs -= 35 offs -= 35
_gameutils.animate_array(self._text_node, 'position', 2, { animate_array(self._text_node, 'position', 2, {
0: self._text_node.position, 0: self._text_node.position,
0.1: (-100 + offs, self._vpos + 23) 0.1: (-100 + offs, self._vpos + 23)
}) })
_gameutils.animate_array(self.icon, 'position', 2, { animate_array(self.icon, 'position', 2, {
0: self.icon.position, 0: self.icon.position,
0.1: (-130 + offs, self._vpos + 22) 0.1: (-130 + offs, self._vpos + 22)
}) })
def get_character_name(self) -> str: def get_character_name(self) -> str:
"""Return the selected character name.""" """Return the selected character name."""
return self.character_names[self.character_index] return self._character_names[self._character_index]
def _do_nothing(self) -> None: def _do_nothing(self) -> None:
"""Does nothing! (hacky way to disable callbacks)""" """Does nothing! (hacky way to disable callbacks)"""
def _get_name(self, full: bool = False) -> str: def _get_name(self, full: bool = False) -> str:
# FIXME: Needs cleanup.
# pylint: disable=too-many-branches
from ba._lang import Lstr
from ba._enums import SpecialChar
name_raw = name = self._profilenames[self._profileindex] name_raw = name = self._profilenames[self._profileindex]
clamp = False clamp = False
if name == '_random': if name == '_random':
input_device: Optional[ba.InputDevice]
try: try:
input_device = self._player.get_input_device() name = (self._player.inputdevice.get_default_player_name())
except Exception: except Exception:
input_device = None print_exception('Error getting _random chooser name.')
if input_device is not None:
name = input_device.get_default_player_name()
else:
name = 'Invalid' name = 'Invalid'
if not full: clamp = not full
clamp = True
elif name == '__account__': elif name == '__account__':
try: try:
input_device = self._player.get_input_device() name = self._player.inputdevice.get_account_name(full)
except Exception: except Exception:
input_device = None print_exception('Error getting account name for chooser.')
if input_device is not None:
name = input_device.get_account_name(full)
else:
name = 'Invalid' name = 'Invalid'
if not full: clamp = not full
clamp = True
elif name == '_edit': elif name == '_edit':
# FIXME: This causes problems as an Lstr, but its ok to # Explicitly flattening this to a str; it's only relevant on
# explicitly translate for now since this is only shown on the # the host so that's ok.
# host. (also should elaborate; don't remember what problems this
# caused)
name = (Lstr( name = (Lstr(
resource='createEditPlayerText', resource='createEditPlayerText',
fallback_resource='editProfileWindow.titleNewText').evaluate()) fallback_resource='editProfileWindow.titleNewText').evaluate())
else: else:
# If we have a regular profile marked as global with an icon, # If we have a regular profile marked as global with an icon,
# use it (for full only). # use it (for full only).
if full: if full:
try: try:
if self.profiles[name_raw].get('global', False): if self._profiles[name_raw].get('global', False):
icon = (self.profiles[name_raw]['icon'] icon = (self._profiles[name_raw]['icon']
if 'icon' in self.profiles[name_raw] else if 'icon' in self._profiles[name_raw] else
_ba.charstr(SpecialChar.LOGO)) _ba.charstr(SpecialChar.LOGO))
name = icon + name name = icon + name
except Exception: except Exception:
from ba import _error print_exception('Error applying global icon.')
_error.print_exception('Error applying global icon')
else: else:
# We now clamp non-full versions of names so there's at # We now clamp non-full versions of names so there's at
# least some hope of reading them in-game. # least some hope of reading them in-game.
clamp = True clamp = True
@ -561,9 +511,9 @@ class Chooser:
with _ba.Context('ui'): with _ba.Context('ui'):
pbrowser.ProfileBrowserWindow(in_main_menu=False) pbrowser.ProfileBrowserWindow(in_main_menu=False)
# give their input-device UI ownership too # Give their input-device UI ownership too
# (prevent someone else from snatching it in crowded games) # (prevent someone else from snatching it in crowded games)
_ba.set_ui_input_device(self._player.get_input_device()) _ba.set_ui_input_device(self._player.inputdevice)
return return
if not ready: if not ready:
@ -597,7 +547,7 @@ class Chooser:
Call(self.handlemessage, ChangeMessage('ready', 0))) Call(self.handlemessage, ChangeMessage('ready', 0)))
# Store the last profile picked by this input for reuse. # Store the last profile picked by this input for reuse.
input_device = self._player.get_input_device() input_device = self._player.inputdevice
name = input_device.name name = input_device.name
unique_id = input_device.unique_identifier unique_id = input_device.unique_identifier
device_profiles = _ba.app.config.setdefault( device_profiles = _ba.app.config.setdefault(
@ -607,7 +557,8 @@ class Chooser:
# to random; in that case we'll want to start picking up custom # to random; in that case we'll want to start picking up custom
# profiles if/when one is made so keep our setting cleared. # profiles if/when one is made so keep our setting cleared.
special = ('_random', '_edit', '__account__') special = ('_random', '_edit', '__account__')
have_custom_profiles = any(p not in special for p in self.profiles) have_custom_profiles = any(p not in special
for p in self._profiles)
profilekey = name + ' ' + unique_id profilekey = name + ' ' + unique_id
if profilename == '_random' and not have_custom_profiles: if profilename == '_random' and not have_custom_profiles:
@ -643,7 +594,7 @@ class Chooser:
# choosers that have been marked as ready. # choosers that have been marked as ready.
team_player_counts = {} team_player_counts = {}
for team in teams: for team in teams:
team_player_counts[team.id] = (len(team.players)) team_player_counts[team.id] = len(team.players)
for chooser in lobby.choosers: for chooser in lobby.choosers:
if chooser.ready: if chooser.ready:
team_player_counts[chooser.get_team().id] += 1 team_player_counts[chooser.get_team().id] += 1
@ -668,15 +619,14 @@ class Chooser:
# TODO: should handle this at the engine layer so this is unnecessary. # TODO: should handle this at the engine layer so this is unnecessary.
def _handle_repeat_message_attack(self) -> None: def _handle_repeat_message_attack(self) -> None:
now = _ba.time() now = _ba.time()
count = self.last_change[1] count = self._last_change[1]
if now - self.last_change[0] < QUICK_CHANGE_INTERVAL: if now - self._last_change[0] < QUICK_CHANGE_INTERVAL:
count += 1 count += 1
if count > MAX_QUICK_CHANGE_COUNT: if count > MAX_QUICK_CHANGE_COUNT:
_ba.disconnect_client( _ba.disconnect_client(self._player.inputdevice.client_id)
self._player.get_input_device().client_id) elif now - self._last_change[0] > QUICK_CHANGE_RESET_INTERVAL:
elif now - self.last_change[0] > QUICK_CHANGE_RESET_INTERVAL:
count = 0 count = 0
self.last_change = (now, count) self._last_change = (now, count)
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:
"""Standard generic message handler.""" """Standard generic message handler."""
@ -686,13 +636,11 @@ class Chooser:
# If we've been removed from the lobby, ignore this stuff. # If we've been removed from the lobby, ignore this stuff.
if self._dead: if self._dead:
from ba import _error print_error('chooser got ChangeMessage after dying')
_error.print_error('chooser got ChangeMessage after dying')
return return
if not self._text_node: if not self._text_node:
from ba import _error print_error('got ChangeMessage after nodes died')
_error.print_error('got ChangeMessage after nodes died')
return return
if msg.what == 'team': if msg.what == 'team':
@ -718,13 +666,13 @@ class Chooser:
_ba.playsound(self._deek_sound) _ba.playsound(self._deek_sound)
self._profileindex = ((self._profileindex + msg.value) % self._profileindex = ((self._profileindex + msg.value) %
len(self._profilenames)) len(self._profilenames))
self.update_from_player_profiles() self.update_from_profile()
elif msg.what == 'character': elif msg.what == 'character':
_ba.playsound(self._click_sound) _ba.playsound(self._click_sound)
# update our index in our local list of characters # update our index in our local list of characters
self.character_index = ((self.character_index + msg.value) % self._character_index = ((self._character_index + msg.value) %
len(self.character_names)) len(self._character_names))
self._update_text() self._update_text()
self._update_icon() self._update_icon()
@ -732,8 +680,6 @@ class Chooser:
self._handle_ready_msg(bool(msg.value)) self._handle_ready_msg(bool(msg.value))
def _update_text(self) -> None: def _update_text(self) -> None:
from ba import _gameutils
from ba._lang import Lstr
assert self._text_node is not None assert self._text_node is not None
if self._ready: if self._ready:
@ -751,7 +697,7 @@ class Chooser:
# Flash as we're coming in. # Flash as we're coming in.
fin_color = _ba.safecolor(self.get_color()) + (1, ) fin_color = _ba.safecolor(self.get_color()) + (1, )
if not self._inited: if not self._inited:
_gameutils.animate_array(self._text_node, 'color', 4, { animate_array(self._text_node, 'color', 4, {
0.15: fin_color, 0.15: fin_color,
0.25: (2, 2, 2, 1), 0.25: (2, 2, 2, 1),
0.35: fin_color 0.35: fin_color
@ -760,7 +706,7 @@ class Chooser:
# Blend if we're in teams mode; switch instantly otherwise. # Blend if we're in teams mode; switch instantly otherwise.
if can_switch_teams: if can_switch_teams:
_gameutils.animate_array(self._text_node, 'color', 4, { animate_array(self._text_node, 'color', 4, {
0: self._text_node.color, 0: self._text_node.color,
0.1: fin_color 0.1: fin_color
}) })
@ -772,8 +718,6 @@ class Chooser:
def get_color(self) -> Sequence[float]: def get_color(self) -> Sequence[float]:
"""Return the currently selected color.""" """Return the currently selected color."""
val: Sequence[float] val: Sequence[float]
# if self._profilenames[self._profileindex] == '_edit':
# val = (0, 1, 0)
if self.lobby.use_team_colors: if self.lobby.use_team_colors:
val = self.lobby.teams[self._selected_team_index].color val = self.lobby.teams[self._selected_team_index].color
else: else:
@ -819,7 +763,6 @@ class Chooser:
return self._player return self._player
def _update_icon(self) -> None: def _update_icon(self) -> None:
from ba import _gameutils
if self._profilenames[self._profileindex] == '_edit': if self._profilenames[self._profileindex] == '_edit':
tex = _ba.gettexture('black') tex = _ba.gettexture('black')
tint_tex = _ba.gettexture('black') tint_tex = _ba.gettexture('black')
@ -830,13 +773,12 @@ class Chooser:
return return
try: try:
tex_name = (_ba.app.spaz_appearances[self.character_names[ tex_name = (_ba.app.spaz_appearances[self._character_names[
self.character_index]].icon_texture) self._character_index]].icon_texture)
tint_tex_name = (_ba.app.spaz_appearances[self.character_names[ tint_tex_name = (_ba.app.spaz_appearances[self._character_names[
self.character_index]].icon_mask_texture) self._character_index]].icon_mask_texture)
except Exception: except Exception:
from ba import _error print_exception('Error updating char icon list')
_error.print_exception('Error updating char icon list')
tex_name = 'neoSpazIcon' tex_name = 'neoSpazIcon'
tint_tex_name = 'neoSpazIconColorMask' tint_tex_name = 'neoSpazIconColorMask'
@ -853,7 +795,7 @@ class Chooser:
# If we're initing, flash. # If we're initing, flash.
if not self._inited: if not self._inited:
_gameutils.animate_array(self.icon, 'color', 3, { animate_array(self.icon, 'color', 3, {
0.15: (1, 1, 1), 0.15: (1, 1, 1),
0.25: (2, 2, 2), 0.25: (2, 2, 2),
0.35: (1, 1, 1) 0.35: (1, 1, 1)
@ -861,7 +803,7 @@ class Chooser:
# Blend in teams mode; switch instantly in ffa-mode. # Blend in teams mode; switch instantly in ffa-mode.
if can_switch_teams: if can_switch_teams:
_gameutils.animate_array(self.icon, 'tint_color', 3, { animate_array(self.icon, 'tint_color', 3, {
0: self.icon.tint_color, 0: self.icon.tint_color,
0.1: clr 0.1: clr
}) })
@ -963,10 +905,9 @@ class Lobby:
for chooser in self.choosers: for chooser in self.choosers:
try: try:
chooser.reload_profiles() chooser.reload_profiles()
chooser.update_from_player_profiles() chooser.update_from_profile()
except Exception: except Exception:
from ba import _error print_exception('Error reloading profiles.')
_error.print_exception('error reloading profiles')
def update_positions(self) -> None: def update_positions(self) -> None:
"""Update positions for all choosers.""" """Update positions for all choosers."""
@ -1005,11 +946,9 @@ class Lobby:
self.choosers.remove(chooser) self.choosers.remove(chooser)
break break
if not found: if not found:
from ba import _error print_error(f'remove_chooser did not find player {player}')
_error.print_error(f'remove_chooser did not find player {player}')
elif chooser in self.choosers: elif chooser in self.choosers:
from ba import _error print_error(f'chooser remains after removal for {player}')
_error.print_error(f'chooser remains after removal for {player}')
self.update_positions() self.update_positions()
def remove_all_choosers(self) -> None: def remove_all_choosers(self) -> None:

View File

@ -44,14 +44,14 @@ class PowerupMessage:
The type of powerup to be granted (a string). The type of powerup to be granted (a string).
See ba.Powerup.poweruptype for available type values. See ba.Powerup.poweruptype for available type values.
source_node sourcenode
The node the powerup game from, or None otherwise. The node the powerup game from, or None otherwise.
If a powerup is accepted, a ba.PowerupAcceptMessage should be sent If a powerup is accepted, a ba.PowerupAcceptMessage should be sent
back to the source_node to inform it of the fact. This will generally back to the sourcenode to inform it of the fact. This will generally
cause the powerup box to make a sound and disappear or whatnot. cause the powerup box to make a sound and disappear or whatnot.
""" """
poweruptype: str poweruptype: str
source_node: Optional[ba.Node] = None sourcenode: Optional[ba.Node] = None
@dataclass @dataclass

View File

@ -224,12 +224,12 @@ class Session:
# Print a rejection message *only* to the client trying to # Print a rejection message *only* to the client trying to
# join (prevents spamming everyone else in the game). # join (prevents spamming everyone else in the game).
_ba.playsound(_ba.getsound('error')) _ba.playsound(_ba.getsound('error'))
_ba.screenmessage( _ba.screenmessage(Lstr(resource='playerLimitReachedText',
Lstr(resource='playerLimitReachedText', subs=[('${COUNT}',
subs=[('${COUNT}', str(self.max_players))]), str(self.max_players))]),
color=(0.8, 0.0, 0.0), color=(0.8, 0.0, 0.0),
clients=[player.get_input_device().client_id], clients=[player.inputdevice.client_id],
transient=True) transient=True)
return False return False
_ba.playsound(_ba.getsound('dripity')) _ba.playsound(_ba.getsound('dripity'))

View File

@ -601,7 +601,7 @@ class Blast(ba.Actor):
self.node.delete() self.node.delete()
elif isinstance(msg, ExplodeHitMessage): elif isinstance(msg, ExplodeHitMessage):
node = ba.getcollision().opposing_node node = ba.getcollision().opposingnode
assert self.node assert self.node
nodepos = self.node.position nodepos = self.node.position
mag = 2000.0 mag = 2000.0
@ -843,7 +843,7 @@ class Bomb(ba.Actor):
self.handlemessage(ba.DieMessage()) self.handlemessage(ba.DieMessage())
def _handle_impact(self) -> None: def _handle_impact(self) -> None:
node = ba.getcollision().opposing_node node = ba.getcollision().opposingnode
# If we're an impact bomb and we came from this node, don't explode... # If we're an impact bomb and we came from this node, don't explode...
# alternately if we're hitting another impact-bomb from the same # alternately if we're hitting another impact-bomb from the same
@ -875,7 +875,7 @@ class Bomb(ba.Actor):
lambda: _safesetattr(self.node, 'stick_to_owner', True)) lambda: _safesetattr(self.node, 'stick_to_owner', True))
def _handle_splat(self) -> None: def _handle_splat(self) -> None:
node = ba.getcollision().opposing_node node = ba.getcollision().opposingnode
if (node is not self.owner if (node is not self.owner
and ba.time() - self._last_sticky_sound_time > 1.0): and ba.time() - self._last_sticky_sound_time > 1.0):
self._last_sticky_sound_time = ba.time() self._last_sticky_sound_time = ba.time()

View File

@ -264,16 +264,14 @@ class ControlsGuide(ba.Actor):
# If we have a touchscreen, we only fade in if we have a player with # If we have a touchscreen, we only fade in if we have a player with
# an input device that is *not* the touchscreen. # an input device that is *not* the touchscreen.
touchscreen: Optional[ba.InputDevice] = _ba.get_input_device( touchscreen: Optional[ba.InputDevice] = _ba.getinputdevice(
'TouchScreen', '#1', doraise=False) 'TouchScreen', '#1', doraise=False)
if touchscreen is not None: if touchscreen is not None:
# We look at the session's players; not the activity's. # We look at the session's players; not the activity's.
# We want to get ones who are still in the process of # We want to get ones who are still in the process of
# selecting a character, etc. # selecting a character, etc.
input_devices = [ input_devices = [p.inputdevice for p in ba.getsession().players]
p.get_input_device() for p in ba.getsession().players
]
input_devices = [ input_devices = [
i for i in input_devices if i and i is not touchscreen i for i in input_devices if i and i is not touchscreen
] ]
@ -325,13 +323,13 @@ class ControlsGuide(ba.Actor):
# We look at the session's players; not the activity's - we want to # We look at the session's players; not the activity's - we want to
# get ones who are still in the process of selecting a character, etc. # get ones who are still in the process of selecting a character, etc.
input_devices = [p.get_input_device() for p in ba.getsession().players] input_devices = [p.inputdevice for p in ba.getsession().players]
input_devices = [i for i in input_devices if i] input_devices = [i for i in input_devices if i]
# If there's no players with input devices yet, try to default to # If there's no players with input devices yet, try to default to
# showing keyboard controls. # showing keyboard controls.
if not input_devices: if not input_devices:
kbd = _ba.get_input_device('Keyboard', '#1', doraise=False) kbd = _ba.getinputdevice('Keyboard', '#1', doraise=False)
if kbd is not None: if kbd is not None:
input_devices.append(kbd) input_devices.append(kbd)

View File

@ -305,9 +305,9 @@ class PowerupBox(ba.Actor):
elif isinstance(msg, _TouchedMessage): elif isinstance(msg, _TouchedMessage):
if not self._powersgiven: if not self._powersgiven:
node = ba.getcollision().opposing_node node = ba.getcollision().opposingnode
node.handlemessage( node.handlemessage(
ba.PowerupMessage(self.poweruptype, source_node=self.node)) ba.PowerupMessage(self.poweruptype, sourcenode=self.node))
elif isinstance(msg, ba.DieMessage): elif isinstance(msg, ba.DieMessage):
if self.node: if self.node:

View File

@ -851,8 +851,8 @@ class Spaz(ba.Actor):
self._num_times_hit = 0 self._num_times_hit = 0
self.node.handlemessage('flash') self.node.handlemessage('flash')
if msg.source_node: if msg.sourcenode:
msg.source_node.handlemessage(ba.PowerupAcceptMessage()) msg.sourcenode.handlemessage(ba.PowerupAcceptMessage())
return True return True
elif isinstance(msg, ba.FreezeMessage): elif isinstance(msg, ba.FreezeMessage):
@ -1143,7 +1143,7 @@ class Spaz(ba.Actor):
elif isinstance(msg, PunchHitMessage): elif isinstance(msg, PunchHitMessage):
if not self.node: if not self.node:
return None return None
node = ba.getcollision().opposing_node node = ba.getcollision().opposingnode
# Only allow one hit per node per punch. # Only allow one hit per node per punch.
if node and (node not in self._punched_nodes): if node and (node not in self._punched_nodes):
@ -1204,23 +1204,23 @@ class Spaz(ba.Actor):
try: try:
collision = ba.getcollision() collision = ba.getcollision()
opposing_node = collision.opposing_node opposingnode = collision.opposingnode
opposing_body = collision.opposing_body opposingbody = collision.opposingbody
except ba.NotFoundError: except ba.NotFoundError:
return True return True
# Don't allow picking up of invincible dudes. # Don't allow picking up of invincible dudes.
try: try:
if opposing_node.invincible: if opposingnode.invincible:
return True return True
except Exception: except Exception:
pass pass
# If we're grabbing the pelvis of a non-shattered spaz, we wanna # If we're grabbing the pelvis of a non-shattered spaz, we wanna
# grab the torso instead. # grab the torso instead.
if (opposing_node.getnodetype() == 'spaz' if (opposingnode.getnodetype() == 'spaz'
and not opposing_node.shattered and opposing_body == 4): and not opposingnode.shattered and opposingbody == 4):
opposing_body = 1 opposingbody = 1
# Special case - if we're holding a flag, don't replace it # Special case - if we're holding a flag, don't replace it
# (hmm - should make this customizable or more low level). # (hmm - should make this customizable or more low level).
@ -1229,8 +1229,8 @@ class Spaz(ba.Actor):
return True return True
# Note: hold_body needs to be set before hold_node. # Note: hold_body needs to be set before hold_node.
self.node.hold_body = opposing_body self.node.hold_body = opposingbody
self.node.hold_node = opposing_node self.node.hold_node = opposingnode
elif isinstance(msg, ba.CelebrateMessage): elif isinstance(msg, ba.CelebrateMessage):
if self.node: if self.node:
self.node.handlemessage('celebrate', int(msg.duration * 1000)) self.node.handlemessage('celebrate', int(msg.duration * 1000))

View File

@ -179,7 +179,7 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
ba.timer(length, light.delete) ba.timer(length, light.delete)
def _handle_base_collide(self, team: Team) -> None: def _handle_base_collide(self, team: Team) -> None:
player = ba.getcollision().opposing_node.getdelegate( player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player) PlayerSpaz, True).getplayer(Player)
if not player or not player.is_alive(): if not player or not player.is_alive():
return return

View File

@ -158,9 +158,9 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._scoreboard = Scoreboard() self._scoreboard = Scoreboard()
self._alarmsound = ba.getsound('alarm') self._alarmsound = ba.getsound('alarm')
self._ticking_sound = ba.getsound('ticking') self._ticking_sound = ba.getsound('ticking')
self._last_score_time = 0
self._score_sound = ba.getsound('score') self._score_sound = ba.getsound('score')
self._swipsound = ba.getsound('swip') self._swipsound = ba.getsound('swip')
self._last_score_time = 0
self._all_bases_material = ba.Material() self._all_bases_material = ba.Material()
self._last_home_flag_notice_print_time = 0.0 self._last_home_flag_notice_print_time = 0.0
self._score_to_win = int(settings['Score to Win']) self._score_to_win = int(settings['Score to Win'])
@ -237,20 +237,22 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
)) ))
# Other parts of our spazzes don't collide with our flags at all. # Other parts of our spazzes don't collide with our flags at all.
spaz_mat_no_flag_collide.add_actions(conditions=('they_have_material', spaz_mat_no_flag_collide.add_actions(
flagmat), conditions=('they_have_material', flagmat),
actions=('modify_part_collision', actions=('modify_part_collision', 'collide', False),
'collide', False)) )
# We wanna know when *any* flag enters/leaves our base. # We wanna know when *any* flag enters/leaves our base.
base_region_mat.add_actions( base_region_mat.add_actions(
conditions=('they_have_material', FlagFactory.get().flagmaterial), conditions=('they_have_material', FlagFactory.get().flagmaterial),
actions=(('modify_part_collision', 'collide', actions=(
True), ('modify_part_collision', 'physical', False), ('modify_part_collision', 'collide', True),
('call', 'at_connect', ('modify_part_collision', 'physical', False),
lambda: self._handle_flag_entered_base(team)), ('call', 'at_connect',
('call', 'at_disconnect', lambda: self._handle_flag_entered_base(team)),
lambda: self._handle_flag_left_base(team)))) ('call', 'at_disconnect',
lambda: self._handle_flag_left_base(team)),
))
return team return team
@ -274,7 +276,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
ba.playsound(self._swipsound, position=team.flag.node.position) ba.playsound(self._swipsound, position=team.flag.node.position)
def _handle_flag_entered_base(self, team: Team) -> None: def _handle_flag_entered_base(self, team: Team) -> None:
flag = CTFFlag.from_node(ba.getcollision().opposing_node) flag = CTFFlag.from_node(ba.getcollision().opposingnode)
if not flag: if not flag:
print('Unable to get flag in _handle_flag_entered_base') print('Unable to get flag in _handle_flag_entered_base')
return return
@ -362,7 +364,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._flash_base(team) self._flash_base(team)
self._update_scoreboard() self._update_scoreboard()
# Have teammates celebrate # Have teammates celebrate.
for player in team.players: for player in team.players:
if player.actor: if player.actor:
player.actor.handlemessage(ba.CelebrateMessage(2.0)) player.actor.handlemessage(ba.CelebrateMessage(2.0))
@ -385,7 +387,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
def _handle_flag_left_base(self, team: Team) -> None: def _handle_flag_left_base(self, team: Team) -> None:
cur_time = ba.time() cur_time = ba.time()
try: try:
flag = CTFFlag.from_node(ba.getcollision().opposing_node) flag = CTFFlag.from_node(ba.getcollision().opposingnode)
except ba.NodeNotFoundError: except ba.NodeNotFoundError:
# We still get this call even if the flag stopped touching us # We still get this call even if the flag stopped touching us
# because it was deleted; that's ok. # because it was deleted; that's ok.
@ -447,11 +449,12 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
return None if delegate is None else delegate.getplayer(Player) return None if delegate is None else delegate.getplayer(Player)
def _handle_hit_own_flag(self, team: Team, val: int) -> None: def _handle_hit_own_flag(self, team: Team, val: int) -> None:
"""Called when a player touches their own team flag.
We keep track of when each player is touching their
own flag so we can award points when returned.
""" """
keep track of when each player is touching their player = self._player_from_node(ba.getcollision().sourcenode)
own flag so we can award points when returned
"""
player = self._player_from_node(ba.getcollision().source_node)
if player: if player:
player.touching_own_flag += (1 if val else -1) player.touching_own_flag += (1 if val else -1)
@ -464,7 +467,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
# Use a node message to kill the flag instead of just killing # Use a node message to kill the flag instead of just killing
# our team's. (avoids redundantly killing new flags if # our team's. (avoids redundantly killing new flags if
# multiple body parts generate callbacks in one step). # multiple body parts generate callbacks in one step).
node = ba.getcollision().opposing_node node = ba.getcollision().opposingnode
self._award_players_touching_own_flag(team) self._award_players_touching_own_flag(team)
node.handlemessage(ba.DieMessage()) node.handlemessage(ba.DieMessage())
@ -484,8 +487,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
team.touch_return_timer = None team.touch_return_timer = None
team.touch_return_timer_ticking = None team.touch_return_timer_ticking = None
if team.flag_return_touches < 0: if team.flag_return_touches < 0:
ba.print_error( ba.print_error('CTF flag_return_touches < 0')
"CTF: flag_return_touches < 0; this shouldn't happen.")
def _flash_base(self, team: Team, length: float = 2.0) -> None: def _flash_base(self, team: Team, length: float = 2.0) -> None:
light = ba.newnode('light', light = ba.newnode('light',
@ -536,13 +538,15 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._score_to_win) self._score_to_win)
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, ba.PlayerDiedMessage): if isinstance(msg, ba.PlayerDiedMessage):
# Augment standard behavior. super().handlemessage(msg) # Augment standard behavior.
super().handlemessage(msg)
self.respawn_player(msg.getplayer(Player)) self.respawn_player(msg.getplayer(Player))
elif isinstance(msg, FlagDiedMessage): elif isinstance(msg, FlagDiedMessage):
assert isinstance(msg.flag, CTFFlag) assert isinstance(msg.flag, CTFFlag)
ba.timer(0.1, ba.Call(self._spawn_flag_for_team, msg.flag.team)) ba.timer(0.1, ba.Call(self._spawn_flag_for_team, msg.flag.team))
elif isinstance(msg, FlagPickedUpMessage): elif isinstance(msg, FlagPickedUpMessage):
# Store the last player to hold the flag for scoring purposes. # Store the last player to hold the flag for scoring purposes.
assert isinstance(msg.flag, CTFFlag) assert isinstance(msg.flag, CTFFlag)
@ -550,9 +554,11 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
PlayerSpaz, True).getplayer(Player) PlayerSpaz, True).getplayer(Player)
msg.flag.held_count += 1 msg.flag.held_count += 1
msg.flag.reset_return_times() msg.flag.reset_return_times()
elif isinstance(msg, FlagDroppedMessage): elif isinstance(msg, FlagDroppedMessage):
# Store the last player to hold the flag for scoring purposes. # Store the last player to hold the flag for scoring purposes.
assert isinstance(msg.flag, CTFFlag) assert isinstance(msg.flag, CTFFlag)
msg.flag.held_count -= 1 msg.flag.held_count -= 1
else: else:
super().handlemessage(msg) super().handlemessage(msg)

View File

@ -177,7 +177,7 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
# If we have a chosen one, ignore these. # If we have a chosen one, ignore these.
if self._get_chosen_one_player() is not None: if self._get_chosen_one_player() is not None:
return return
player = ba.getcollision().opposing_node.getdelegate( player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player) PlayerSpaz, True).getplayer(Player)
if player is not None and player.is_alive(): if player is not None and player.is_alive():
self._set_chosen_one_player(player) self._set_chosen_one_player(player)

View File

@ -235,9 +235,9 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def _handle_flag_player_collide(self) -> None: def _handle_flag_player_collide(self) -> None:
collision = ba.getcollision() collision = ba.getcollision()
flag = collision.source_node.getdelegate(ConquestFlag) flag = collision.sourcenode.getdelegate(ConquestFlag)
player = collision.opposing_node.getdelegate(PlayerSpaz, player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(Player) True).getplayer(Player)
if not flag or not player: if not flag or not player:
return return
assert flag.light assert flag.light

View File

@ -137,9 +137,9 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
if self.has_ended(): if self.has_ended():
return return
collision = ba.getcollision() collision = ba.getcollision()
egg = collision.source_node.getdelegate(Egg) egg = collision.sourcenode.getdelegate(Egg)
player = collision.opposing_node.getdelegate(PlayerSpaz, player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(Player) True).getplayer(Player)
if player and egg: if player and egg:
player.team.score += 1 player.team.score += 1

View File

@ -209,7 +209,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
assert self._flag is not None assert self._flag is not None
if self._flag.scored: if self._flag.scored:
return return
region = ba.getcollision().source_node region = ba.getcollision().sourcenode
i = None i = None
for i in range(len(self._score_regions)): for i in range(len(self._score_regions)):
if region == self._score_regions[i].node: if region == self._score_regions[i].node:
@ -654,7 +654,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
return return
# See which score region it was. # See which score region it was.
region = ba.getcollision().source_node region = ba.getcollision().sourcenode
i = None i = None
for i in range(len(self._score_regions)): for i in range(len(self._score_regions)):
if region == self._score_regions[i].node: if region == self._score_regions[i].node:

View File

@ -245,9 +245,9 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
def _handle_puck_player_collide(self) -> None: def _handle_puck_player_collide(self) -> None:
collision = ba.getcollision() collision = ba.getcollision()
puck = collision.source_node.getdelegate(Puck) puck = collision.sourcenode.getdelegate(Puck)
player = collision.opposing_node.getdelegate(PlayerSpaz, player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(Player) True).getplayer(Player)
if player and puck: if player and puck:
puck.last_players_to_touch[player.team.id] = player puck.last_players_to_touch[player.team.id] = player
@ -265,7 +265,7 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
if self._puck.scored: if self._puck.scored:
return return
region = ba.getcollision().source_node region = ba.getcollision().sourcenode
index = 0 index = 0
for index in range(len(self._score_regions)): for index in range(len(self._score_regions)):
if region == self._score_regions[index].node: if region == self._score_regions[index].node:

View File

@ -247,7 +247,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
ba.playsound(self._swipsound) ba.playsound(self._swipsound)
def _handle_player_flag_region_collide(self, colliding: bool) -> None: def _handle_player_flag_region_collide(self, colliding: bool) -> None:
player = ba.getcollision().opposing_node.getdelegate( player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player) PlayerSpaz, True).getplayer(Player)
if not player: if not player:
return return

View File

@ -227,9 +227,9 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
# pylint: disable=too-many-nested-blocks # pylint: disable=too-many-nested-blocks
collision = ba.getcollision() collision = ba.getcollision()
region = collision.source_node.getdelegate(RaceRegion) region = collision.sourcenode.getdelegate(RaceRegion)
playerspaz = collision.opposing_node.getdelegate(PlayerSpaz, playerspaz = collision.opposingnode.getdelegate(PlayerSpaz,
doraise=False) doraise=False)
player = playerspaz.getplayer(Player) if playerspaz else None player = playerspaz.getplayer(Player) if playerspaz else None
if not player or not region: if not player or not region:
return return

View File

@ -406,7 +406,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
ba.timer(2.0, self._start_updating_waves) ba.timer(2.0, self._start_updating_waves)
def _handle_reached_end(self) -> None: def _handle_reached_end(self) -> None:
spaz = ba.getcollision().opposing_node.getdelegate(SpazBot, True) spaz = ba.getcollision().opposingnode.getdelegate(SpazBot, True)
if not spaz.is_alive(): if not spaz.is_alive():
return # Ignore bodies flying in. return # Ignore bodies flying in.

View File

@ -91,7 +91,7 @@ class ControlsSettingsWindow(ba.Window):
height += space_height height += space_height
show_keyboard = False show_keyboard = False
if _ba.get_input_device('Keyboard', '#1', doraise=False) is not None: if _ba.getinputdevice('Keyboard', '#1', doraise=False) is not None:
show_keyboard = True show_keyboard = True
height += spacing * 2 height += spacing * 2
show_keyboard_p2 = False if app.vr_mode else show_keyboard show_keyboard_p2 = False if app.vr_mode else show_keyboard
@ -393,7 +393,7 @@ class ControlsSettingsWindow(ba.Window):
self._save_state() self._save_state()
ba.containerwidget(edit=self._root_widget, transition='out_left') ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.main_menu_window = (keyboard.ConfigKeyboardWindow( ba.app.main_menu_window = (keyboard.ConfigKeyboardWindow(
_ba.get_input_device('Keyboard', '#1')).get_root_widget()) _ba.getinputdevice('Keyboard', '#1')).get_root_widget())
def _config_keyboard2(self) -> None: def _config_keyboard2(self) -> None:
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
@ -401,7 +401,7 @@ class ControlsSettingsWindow(ba.Window):
self._save_state() self._save_state()
ba.containerwidget(edit=self._root_widget, transition='out_left') ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.main_menu_window = (keyboard.ConfigKeyboardWindow( ba.app.main_menu_window = (keyboard.ConfigKeyboardWindow(
_ba.get_input_device('Keyboard', '#2')).get_root_widget()) _ba.getinputdevice('Keyboard', '#2')).get_root_widget())
def _do_mobile_devices(self) -> None: def _do_mobile_devices(self) -> None:
# pylint: disable=cyclic-import # pylint: disable=cyclic-import

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND --> <!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2020-05-25 for Ballistica version 1.5.0 build 20027</em></h4> <h4><em>last updated on 2020-05-25 for Ballistica version 1.5.0 build 20028</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module, <p>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 <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p> 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 <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr> <hr>
@ -189,6 +189,7 @@
<ul> <ul>
<li><a href="#class_ba_ActivityNotFoundError">ba.ActivityNotFoundError</a></li> <li><a href="#class_ba_ActivityNotFoundError">ba.ActivityNotFoundError</a></li>
<li><a href="#class_ba_ActorNotFoundError">ba.ActorNotFoundError</a></li> <li><a href="#class_ba_ActorNotFoundError">ba.ActorNotFoundError</a></li>
<li><a href="#class_ba_DelegateNotFoundError">ba.DelegateNotFoundError</a></li>
<li><a href="#class_ba_InputDeviceNotFoundError">ba.InputDeviceNotFoundError</a></li> <li><a href="#class_ba_InputDeviceNotFoundError">ba.InputDeviceNotFoundError</a></li>
<li><a href="#class_ba_NodeNotFoundError">ba.NodeNotFoundError</a></li> <li><a href="#class_ba_NodeNotFoundError">ba.NodeNotFoundError</a></li>
<li><a href="#class_ba_PlayerNotFoundError">ba.PlayerNotFoundError</a></li> <li><a href="#class_ba_PlayerNotFoundError">ba.PlayerNotFoundError</a></li>
@ -918,7 +919,7 @@ likely result in errors.</p>
<dt><h4><a name="method_ba_App__launch_coop_game">launch_coop_game()</a></dt></h4><dd> <dt><h4><a name="method_ba_App__launch_coop_game">launch_coop_game()</a></dt></h4><dd>
<p><span>launch_coop_game(self, game: str, force: bool = False, args: Dict = None) -&gt; bool</span></p> <p><span>launch_coop_game(self, game: str, force: bool = False, args: Dict = None) -&gt; bool</span></p>
<p>High level way to launch a co-op session locally.</p> <p>High level way to launch a local co-op session.</p>
</dd> </dd>
<dt><h4><a name="method_ba_App__on_app_pause">on_app_pause()</a></dt></h4><dd> <dt><h4><a name="method_ba_App__on_app_pause">on_app_pause()</a></dt></h4><dd>
@ -1298,7 +1299,7 @@ mycall()</pre>
</dd> </dd>
</dl> </dl>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_Chooser____init__">&lt;constructor&gt;</a>, <a href="#method_ba_Chooser__get_character_name">get_character_name()</a>, <a href="#method_ba_Chooser__get_color">get_color()</a>, <a href="#method_ba_Chooser__get_highlight">get_highlight()</a>, <a href="#method_ba_Chooser__get_lobby">get_lobby()</a>, <a href="#method_ba_Chooser__get_team">get_team()</a>, <a href="#method_ba_Chooser__getplayer">getplayer()</a>, <a href="#method_ba_Chooser__handlemessage">handlemessage()</a>, <a href="#method_ba_Chooser__reload_profiles">reload_profiles()</a>, <a href="#method_ba_Chooser__update_from_player_profiles">update_from_player_profiles()</a>, <a href="#method_ba_Chooser__update_position">update_position()</a></h5> <h5><a href="#method_ba_Chooser____init__">&lt;constructor&gt;</a>, <a href="#method_ba_Chooser__get_character_name">get_character_name()</a>, <a href="#method_ba_Chooser__get_color">get_color()</a>, <a href="#method_ba_Chooser__get_highlight">get_highlight()</a>, <a href="#method_ba_Chooser__get_lobby">get_lobby()</a>, <a href="#method_ba_Chooser__get_team">get_team()</a>, <a href="#method_ba_Chooser__getplayer">getplayer()</a>, <a href="#method_ba_Chooser__handlemessage">handlemessage()</a>, <a href="#method_ba_Chooser__reload_profiles">reload_profiles()</a>, <a href="#method_ba_Chooser__update_from_profile">update_from_profile()</a>, <a href="#method_ba_Chooser__update_position">update_position()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_Chooser____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_Chooser____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.Chooser(vpos: float, player: _<a href="#class_ba_SessionPlayer">ba.SessionPlayer</a>, lobby: "Lobby")</span></p> <p><span>ba.Chooser(vpos: float, player: _<a href="#class_ba_SessionPlayer">ba.SessionPlayer</a>, lobby: "Lobby")</span></p>
@ -1352,10 +1353,10 @@ mycall()</pre>
<p>Reload all player profiles.</p> <p>Reload all player profiles.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Chooser__update_from_player_profiles">update_from_player_profiles()</a></dt></h4><dd> <dt><h4><a name="method_ba_Chooser__update_from_profile">update_from_profile()</a></dt></h4><dd>
<p><span>update_from_player_profiles(self) -&gt; None</span></p> <p><span>update_from_profile(self) -&gt; None</span></p>
<p>Set character based on profile; otherwise use pre-picked random.</p> <p>Set character/colors based on the current profile.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Chooser__update_position">update_position()</a></dt></h4><dd> <dt><h4><a name="method_ba_Chooser__update_position">update_position()</a></dt></h4><dd>
@ -1382,14 +1383,14 @@ mycall()</pre>
<p>A class providing info about occurring collisions.</p> <p>A class providing info about occurring collisions.</p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_Collision__opposing_body">opposing_body</a>, <a href="#attr_ba_Collision__opposing_node">opposing_node</a>, <a href="#attr_ba_Collision__position">position</a>, <a href="#attr_ba_Collision__source_node">source_node</a></h5> <h5><a href="#attr_ba_Collision__opposingbody">opposingbody</a>, <a href="#attr_ba_Collision__opposingnode">opposingnode</a>, <a href="#attr_ba_Collision__position">position</a>, <a href="#attr_ba_Collision__sourcenode">sourcenode</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_Collision__opposing_body">opposing_body</a></h4></dt><dd> <dt><h4><a name="attr_ba_Collision__opposingbody">opposingbody</a></h4></dt><dd>
<p><span>int</span></p> <p><span>int</span></p>
<p>The body index on the opposing node in the current collision.</p> <p>The body index on the opposing node in the current collision.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_Collision__opposing_node">opposing_node</a></h4></dt><dd> <dt><h4><a name="attr_ba_Collision__opposingnode">opposingnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p> <p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The node the current callback material node is hitting.</p> <p>The node the current callback material node is hitting.</p>
@ -1403,7 +1404,7 @@ mycall()</pre>
<p>The position of the current collision.</p> <p>The position of the current collision.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_Collision__source_node">source_node</a></h4></dt><dd> <dt><h4><a name="attr_ba_Collision__sourcenode">sourcenode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p> <p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The node containing the material triggering the current callback.</p> <p>The node containing the material triggering the current callback.</p>
@ -1734,6 +1735,16 @@ the data object is requested and when it's value is accessed.</p>
<li>LEFT_GAME</li> <li>LEFT_GAME</li>
</ul> </ul>
<hr> <hr>
<h2><strong><a name="class_ba_DelegateNotFoundError">ba.DelegateNotFoundError</a></strong></h3>
<p>Inherits from: <a href="#class_ba_NotFoundError">ba.NotFoundError</a>, Exception, BaseException</p>
<p>Exception raised when an expected delegate object does not exist.</p>
<p>Category: <a href="#class_category_Exception_Classes">Exception Classes</a>
</p>
<h3>Methods:</h3>
<p>&lt;all methods inherited from <a href="#class_ba_NotFoundError">ba.NotFoundError</a>&gt;</p>
<hr>
<h2><strong><a name="class_ba_Dependency">ba.Dependency</a></strong></h3> <h2><strong><a name="class_ba_Dependency">ba.Dependency</a></strong></h3>
<p>Inherits from: <a href="#class_typing_Generic">typing.Generic</a></p> <p>Inherits from: <a href="#class_typing_Generic">typing.Generic</a></p>
<p>A dependency on a DependencyComponent (with an optional config).</p> <p>A dependency on a DependencyComponent (with an optional config).</p>
@ -3569,8 +3580,8 @@ loc.connectattr('position', light, 'position')</pre>
<dt><h4><a name="method_ba_Node__delete">delete()</a></dt></h4><dd> <dt><h4><a name="method_ba_Node__delete">delete()</a></dt></h4><dd>
<p><span>delete(ignore_missing: bool = True) -&gt; None</span></p> <p><span>delete(ignore_missing: bool = True) -&gt; None</span></p>
<p>Delete the node. Ignores already-deleted nodes unless ignore_missing <p>Delete the node. Ignores already-deleted nodes if ignore_missing
is False, in which case an Exception is thrown.</p> is True; otherwise a <a href="#class_ba_NodeNotFoundError">ba.NodeNotFoundError</a> is thrown.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Node__exists">exists()</a></dt></h4><dd> <dt><h4><a name="method_ba_Node__exists">exists()</a></dt></h4><dd>
@ -3598,7 +3609,7 @@ the right thing both for Node objects and values of None.</p>
<p>If the node has no delegate or it is not an instance of the passed <p>If the node has no delegate or it is not an instance of the passed
type, then None will be returned. If 'doraise' is True, then an type, then None will be returned. If 'doraise' is True, then an
Exception will be raised instead in such cases.</p> <a href="#class_ba_DelegateNotFoundError">ba.DelegateNotFoundError</a> will be raised instead.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Node__getnodetype">getnodetype()</a></dt></h4><dd> <dt><h4><a name="method_ba_Node__getnodetype">getnodetype()</a></dt></h4><dd>
@ -4094,7 +4105,7 @@ the type-checker properly identifies the returned value as one.</p>
<p> This message is normally received by touching a ba.PowerupBox.</p> <p> This message is normally received by touching a ba.PowerupBox.</p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_PowerupMessage__poweruptype">poweruptype</a>, <a href="#attr_ba_PowerupMessage__source_node">source_node</a></h5> <h5><a href="#attr_ba_PowerupMessage__poweruptype">poweruptype</a>, <a href="#attr_ba_PowerupMessage__sourcenode">sourcenode</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_PowerupMessage__poweruptype">poweruptype</a></h4></dt><dd> <dt><h4><a name="attr_ba_PowerupMessage__poweruptype">poweruptype</a></h4></dt><dd>
<p><span>str</span></p> <p><span>str</span></p>
@ -4102,11 +4113,11 @@ the type-checker properly identifies the returned value as one.</p>
See ba.Powerup.poweruptype for available type values.</p> See ba.Powerup.poweruptype for available type values.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_PowerupMessage__source_node">source_node</a></h4></dt><dd> <dt><h4><a name="attr_ba_PowerupMessage__sourcenode">sourcenode</a></h4></dt><dd>
<p><span>Optional[<a href="#class_ba_Node">ba.Node</a>]</span></p> <p><span>Optional[<a href="#class_ba_Node">ba.Node</a>]</span></p>
<p>The node the powerup game from, or None otherwise. <p>The node the powerup game from, or None otherwise.
If a powerup is accepted, a <a href="#class_ba_PowerupAcceptMessage">ba.PowerupAcceptMessage</a> should be sent If a powerup is accepted, a <a href="#class_ba_PowerupAcceptMessage">ba.PowerupAcceptMessage</a> should be sent
back to the source_node to inform it of the fact. This will generally back to the sourcenode to inform it of the fact. This will generally
cause the powerup box to make a sound and disappear or whatnot.</p> cause the powerup box to make a sound and disappear or whatnot.</p>
</dd> </dd>
@ -4114,7 +4125,7 @@ cause the powerup box to make a sound and disappear or whatnot.</p>
<h3>Methods:</h3> <h3>Methods:</h3>
<dl> <dl>
<dt><h4><a name="method_ba_PowerupMessage____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_PowerupMessage____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.PowerupMessage(poweruptype: str, source_node: Optional[<a href="#class_ba_Node">ba.Node</a>] = None)</span></p> <p><span>ba.PowerupMessage(poweruptype: str, sourcenode: Optional[<a href="#class_ba_Node">ba.Node</a>] = None)</span></p>
</dd> </dd>
</dl> </dl>
@ -4443,7 +4454,7 @@ that a SessionPlayer is still present if retaining references to one
for any length of time.</p> for any length of time.</p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_SessionPlayer__character">character</a>, <a href="#attr_ba_SessionPlayer__color">color</a>, <a href="#attr_ba_SessionPlayer__gamedata">gamedata</a>, <a href="#attr_ba_SessionPlayer__gameplayer">gameplayer</a>, <a href="#attr_ba_SessionPlayer__highlight">highlight</a>, <a href="#attr_ba_SessionPlayer__id">id</a>, <a href="#attr_ba_SessionPlayer__in_game">in_game</a>, <a href="#attr_ba_SessionPlayer__sessiondata">sessiondata</a>, <a href="#attr_ba_SessionPlayer__team">team</a></h5> <h5><a href="#attr_ba_SessionPlayer__character">character</a>, <a href="#attr_ba_SessionPlayer__color">color</a>, <a href="#attr_ba_SessionPlayer__gamedata">gamedata</a>, <a href="#attr_ba_SessionPlayer__gameplayer">gameplayer</a>, <a href="#attr_ba_SessionPlayer__highlight">highlight</a>, <a href="#attr_ba_SessionPlayer__id">id</a>, <a href="#attr_ba_SessionPlayer__in_game">in_game</a>, <a href="#attr_ba_SessionPlayer__inputdevice">inputdevice</a>, <a href="#attr_ba_SessionPlayer__sessiondata">sessiondata</a>, <a href="#attr_ba_SessionPlayer__team">team</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_SessionPlayer__character">character</a></h4></dt><dd> <dt><h4><a name="attr_ba_SessionPlayer__character">character</a></h4></dt><dd>
<p><span> str</span></p> <p><span> str</span></p>
@ -4490,6 +4501,11 @@ the right thing both for Player objects and values of None.</p>
<p>This bool value will be True once the Player has completed <p>This bool value will be True once the Player has completed
any lobby character/team selection.</p> any lobby character/team selection.</p>
</dd>
<dt><h4><a name="attr_ba_SessionPlayer__inputdevice">inputdevice</a></h4></dt><dd>
<p><span> <a href="#class_ba_InputDevice">ba.InputDevice</a></span></p>
<p>The input device associated with the player.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_SessionPlayer__sessiondata">sessiondata</a></h4></dt><dd> <dt><h4><a name="attr_ba_SessionPlayer__sessiondata">sessiondata</a></h4></dt><dd>
<p><span> Dict</span></p> <p><span> Dict</span></p>
@ -4507,7 +4523,7 @@ is still in its lobby selecting a team/etc. then a
</dd> </dd>
</dl> </dl>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_SessionPlayer__assign_input_call">assign_input_call()</a>, <a href="#method_ba_SessionPlayer__exists">exists()</a>, <a href="#method_ba_SessionPlayer__get_account_id">get_account_id()</a>, <a href="#method_ba_SessionPlayer__get_icon">get_icon()</a>, <a href="#method_ba_SessionPlayer__get_input_device">get_input_device()</a>, <a href="#method_ba_SessionPlayer__get_name">get_name()</a>, <a href="#method_ba_SessionPlayer__remove_from_game">remove_from_game()</a>, <a href="#method_ba_SessionPlayer__reset_input">reset_input()</a>, <a href="#method_ba_SessionPlayer__set_name">set_name()</a></h5> <h5><a href="#method_ba_SessionPlayer__assign_input_call">assign_input_call()</a>, <a href="#method_ba_SessionPlayer__exists">exists()</a>, <a href="#method_ba_SessionPlayer__get_account_id">get_account_id()</a>, <a href="#method_ba_SessionPlayer__get_icon">get_icon()</a>, <a href="#method_ba_SessionPlayer__get_name">get_name()</a>, <a href="#method_ba_SessionPlayer__remove_from_game">remove_from_game()</a>, <a href="#method_ba_SessionPlayer__reset_input">reset_input()</a>, <a href="#method_ba_SessionPlayer__set_name">set_name()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_SessionPlayer__assign_input_call">assign_input_call()</a></dt></h4><dd> <dt><h4><a name="method_ba_SessionPlayer__assign_input_call">assign_input_call()</a></dt></h4><dd>
<p><span>assign_input_call(type: Union[str, Tuple[str, ...]], <p><span>assign_input_call(type: Union[str, Tuple[str, ...]],
@ -4544,12 +4560,6 @@ joins (while verification occurs).</p>
<p>Returns the character's icon (images, colors, etc contained in a dict)</p> <p>Returns the character's icon (images, colors, etc contained in a dict)</p>
</dd>
<dt><h4><a name="method_ba_SessionPlayer__get_input_device">get_input_device()</a></dt></h4><dd>
<p><span>get_input_device() -&gt; <a href="#class_ba_InputDevice">ba.InputDevice</a></span></p>
<p>Returns the player's input device.</p>
</dd> </dd>
<dt><h4><a name="method_ba_SessionPlayer__get_name">get_name()</a></dt></h4><dd> <dt><h4><a name="method_ba_SessionPlayer__get_name">get_name()</a></dt></h4><dd>
<p><span>get_name(full: bool = False, icon: bool = True) -&gt; str</span></p> <p><span>get_name(full: bool = False, icon: bool = True) -&gt; str</span></p>