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/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb",
"assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe",
"build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e4/f0/c3261a8a7391a2b654da82c35c21",
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/33/e2/85a8cbf23404612e6761cf02348b",
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5b/0d/d906be5fc2a75b5214a94cafe6ca",
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/6b/53/72d665caf5dfed84312ae6862642",
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/68/2d98f2d1ee67b6d54adbdf76c863",
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e5/3a/33f8f311260542126acea44d05e2",
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/18/eb/dd39bcfa33983864089d3fb7f666",
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f8/1d/3440010fcbcf18c5a6b2966f7fca",
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/9b/fa/b4ad4b8b82fefe1faad610065b9f",
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/7f/b9/03af92db6140135ddfa467a1545d",
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/69/b9/687fc5fa38c3faf7a47eb4241a3c",
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/cf/2b/589db924b489972981e10850a4e3"
"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/43/32/c48a4da7fcc8c17132077da852d7",
"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/1f/9a/b112f788f528ec9e3627622698ac",
"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/fa/ba/125457c69bc2940850ab84c4cc6a",
"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/fa/66/75a3a0f27d07131473e804a5b937",
"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/01/cc/cf896173a8d5731ada2aace59146",
"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/e1/5f/f372d0ba9591b99284b9b50d7cdf"
}

View File

@ -480,6 +480,7 @@
<w>downmix</w>
<w>dpad</w>
<w>dpath</w>
<w>dprofilename</w>
<w>drawscore</w>
<w>drawscreen</w>
<w>dripity</w>
@ -756,8 +757,10 @@
<w>getcurrency</w>
<w>getcwd</w>
<w>getdata</w>
<w>getinputdevice</w>
<w>getkillerplayer</w>
<w>getlevelname</w>
<w>getlog</w>
<w>getmaps</w>
<w>getmodel</w>
<w>getnodes</w>
@ -937,6 +940,7 @@
<w>joedeshon</w>
<w>johab</w>
<w>joinable</w>
<w>joinmsg</w>
<w>jovica</w>
<w>jsonstr</w>
<w>jsonstrbase</w>
@ -1267,6 +1271,8 @@
<w>openssh</w>
<w>operasinger</w>
<w>oppnode</w>
<w>opposingbody</w>
<w>opposingnode</w>
<w>opstr</w>
<w>optparse</w>
<w>orchestrahitsound</w>
@ -1504,6 +1510,7 @@
<w>qrencode</w>
<w>qual</w>
<w>quoprimime</w>
<w>rando</w>
<w>randommodule</w>
<w>randval</w>
<w>rankbutton</w>
@ -1672,6 +1679,7 @@
<w>soundtrackname</w>
<w>sourceimages</w>
<w>sourcelines</w>
<w>sourcenode</w>
<w>spacelen</w>
<w>spacingstr</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)
# SOURCES_HASH=164420280597992494471294420110866243586
# SOURCES_HASH=318889180473540875493180206824864665481
# I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression
@ -691,8 +691,8 @@ class Node:
def delete(self, ignore_missing: bool = True) -> None:
"""delete(ignore_missing: bool = True) -> None
Delete the node. Ignores already-deleted nodes unless ignore_missing
is False, in which case an Exception is thrown.
Delete the node. Ignores already-deleted nodes if ignore_missing
is True; otherwise a ba.NodeNotFoundError is thrown.
"""
return None
@ -716,6 +716,7 @@ class Node:
"""
return str()
# Show that ur return type varies based on "doraise" value:
@overload
def getdelegate(self,
type: Type[_T],
@ -733,7 +734,7 @@ class Node:
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
Exception will be raised instead in such cases.
ba.DelegateNotFoundError will be raised instead.
"""
return None
@ -818,6 +819,9 @@ class SessionPlayer:
storing data associated with this Player.
This gets cleared for each new ba.Activity.
inputdevice: ba.InputDevice
The input device associated with the player.
color: Sequence[float]
The base color for this Player.
In team games this will match the ba.SessionTeam's color.
@ -839,6 +843,7 @@ class SessionPlayer:
team: ba.SessionTeam
sessiondata: Dict
gamedata: Dict
inputdevice: ba.InputDevice
color: Sequence[float]
highlight: Sequence[float]
character: str
@ -892,14 +897,6 @@ class SessionPlayer:
"""
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:
"""get_name(full: bool = False, icon: bool = True) -> str
@ -2065,22 +2062,6 @@ def get_idle_time() -> 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:
"""get_local_active_input_devices_count() -> int
@ -2089,14 +2070,6 @@ def get_local_active_input_devices_count() -> int:
return int()
def get_log() -> str:
"""get_log() -> str
(internal)
"""
return str()
def get_log_file_path() -> str:
"""get_log_file_path() -> str
@ -2389,6 +2362,41 @@ def getdata(name: str) -> 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:
"""getmodel(name: str) -> ba.Model

View File

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

View File

@ -22,6 +22,7 @@
from __future__ import annotations
import time
import random
from typing import TYPE_CHECKING
import _ba
@ -376,7 +377,7 @@ class App:
# Lobby.
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
# Main Menu.
@ -722,7 +723,7 @@ class App:
game: str,
force: bool = False,
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
from ba._campaign import get_campaign
from bastd.ui.coop.level import CoopLevelLockedWindow
@ -788,7 +789,7 @@ class App:
color=(1, 1, 0)),
timetype=TimeType.REAL)
def shutdown(self) -> None:
def on_app_shutdown(self) -> None:
"""(internal)"""
self.music.on_app_shutdown()

View File

@ -43,7 +43,7 @@ def is_browser_likely_available() -> bool:
"""
app = _ba.app
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,
# assume no browser.
@ -100,7 +100,7 @@ def handle_log() -> None:
except Exception:
activityname = 'unavailable'
info = {
'log': _ba.get_log(),
'log': _ba.getlog(),
'version': app.version,
'build': app.build_number,
'userAgentString': app.user_agent_string,

View File

@ -40,37 +40,37 @@ class Collision:
return _ba.Vec3(_ba.get_collision_info('position'))
@property
def source_node(self) -> ba.Node:
def sourcenode(self) -> ba.Node:
"""The node containing the material triggering the current callback.
Throws a ba.NodeNotFoundError if the node does not exist, though
the node should always exist (at least at the start of the collision
callback).
"""
node = _ba.get_collision_info('source_node')
node = _ba.get_collision_info('sourcenode')
assert isinstance(node, (_ba.Node, type(None)))
if not node:
raise NodeNotFoundError()
return node
@property
def opposing_node(self) -> ba.Node:
def opposingnode(self) -> ba.Node:
"""The node the current callback material node is hitting.
Throws a ba.NodeNotFoundError if the node does not exist.
This can be expected in some cases such as in 'disconnect'
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)))
if not node:
raise NodeNotFoundError()
return node
@property
def opposing_body(self) -> int:
def opposingbody(self) -> int:
"""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)
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):
"""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
# based time values.
if _ba.app.test_build and not suppress_format_warning:
for item in items:
# (PyCharm seems to think item is a float, not a tuple)
_ba.time_format_check(timeformat, item[0])
if __debug__:
if not suppress_format_warning:
for item in items:
_ba.time_format_check(timeformat, item[0])
curve = _ba.newnode('animcurve',
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
# get disconnected.
if not loop:
# (PyCharm seems to think item is a float, not a tuple)
_ba.timer(int(mult * items[-1][0]) + 1000,
curve.delete,
timeformat=TimeFormat.MILLISECONDS)
@ -259,10 +258,11 @@ def animate_array(node: ba.Node,
# Temp sanity check while we transition from milliseconds to seconds
# based time values.
if _ba.app.test_build and not suppress_format_warning:
for item in items:
# (PyCharm seems to think item is a float, not a tuple)
_ba.time_format_check(timeformat, item[0])
if __debug__:
if not suppress_format_warning:
for item in items:
# (PyCharm seems to think item is a float, not a tuple)
_ba.time_format_check(timeformat, item[0])
if timeformat is TimeFormat.SECONDS:
mult = 1000
@ -381,8 +381,9 @@ def timestring(timeval: float,
# Temp sanity check while we transition from milliseconds to seconds
# based time values.
if _ba.app.test_build and not suppress_format_warning:
_ba.time_format_check(timeformat, timeval)
if __debug__:
if not suppress_format_warning:
_ba.time_format_check(timeformat, timeval)
# We operate on milliseconds internally.
if timeformat is TimeFormat.SECONDS:

View File

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

View File

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

View File

@ -44,14 +44,14 @@ class PowerupMessage:
The type of powerup to be granted (a string).
See ba.Powerup.poweruptype for available type values.
source_node
sourcenode
The node the powerup game from, or None otherwise.
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.
"""
poweruptype: str
source_node: Optional[ba.Node] = None
sourcenode: Optional[ba.Node] = None
@dataclass

View File

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

View File

@ -601,7 +601,7 @@ class Blast(ba.Actor):
self.node.delete()
elif isinstance(msg, ExplodeHitMessage):
node = ba.getcollision().opposing_node
node = ba.getcollision().opposingnode
assert self.node
nodepos = self.node.position
mag = 2000.0
@ -843,7 +843,7 @@ class Bomb(ba.Actor):
self.handlemessage(ba.DieMessage())
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...
# 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))
def _handle_splat(self) -> None:
node = ba.getcollision().opposing_node
node = ba.getcollision().opposingnode
if (node is not self.owner
and ba.time() - self._last_sticky_sound_time > 1.0):
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
# 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)
if touchscreen is not None:
# 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.
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 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
# 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]
# If there's no players with input devices yet, try to default to
# showing keyboard controls.
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:
input_devices.append(kbd)

View File

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

View File

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

View File

@ -158,9 +158,9 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._scoreboard = Scoreboard()
self._alarmsound = ba.getsound('alarm')
self._ticking_sound = ba.getsound('ticking')
self._last_score_time = 0
self._score_sound = ba.getsound('score')
self._swipsound = ba.getsound('swip')
self._last_score_time = 0
self._all_bases_material = ba.Material()
self._last_home_flag_notice_print_time = 0.0
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.
spaz_mat_no_flag_collide.add_actions(conditions=('they_have_material',
flagmat),
actions=('modify_part_collision',
'collide', False))
spaz_mat_no_flag_collide.add_actions(
conditions=('they_have_material', flagmat),
actions=('modify_part_collision', 'collide', False),
)
# We wanna know when *any* flag enters/leaves our base.
base_region_mat.add_actions(
conditions=('they_have_material', FlagFactory.get().flagmaterial),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('call', 'at_connect',
lambda: self._handle_flag_entered_base(team)),
('call', 'at_disconnect',
lambda: self._handle_flag_left_base(team))))
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('call', 'at_connect',
lambda: self._handle_flag_entered_base(team)),
('call', 'at_disconnect',
lambda: self._handle_flag_left_base(team)),
))
return team
@ -274,7 +276,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
ba.playsound(self._swipsound, position=team.flag.node.position)
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:
print('Unable to get flag in _handle_flag_entered_base')
return
@ -362,7 +364,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._flash_base(team)
self._update_scoreboard()
# Have teammates celebrate
# Have teammates celebrate.
for player in team.players:
if player.actor:
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:
cur_time = ba.time()
try:
flag = CTFFlag.from_node(ba.getcollision().opposing_node)
flag = CTFFlag.from_node(ba.getcollision().opposingnode)
except ba.NodeNotFoundError:
# We still get this call even if the flag stopped touching us
# 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)
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
own flag so we can award points when returned
"""
player = self._player_from_node(ba.getcollision().source_node)
player = self._player_from_node(ba.getcollision().sourcenode)
if player:
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
# our team's. (avoids redundantly killing new flags if
# multiple body parts generate callbacks in one step).
node = ba.getcollision().opposing_node
node = ba.getcollision().opposingnode
self._award_players_touching_own_flag(team)
node.handlemessage(ba.DieMessage())
@ -484,8 +487,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
team.touch_return_timer = None
team.touch_return_timer_ticking = None
if team.flag_return_touches < 0:
ba.print_error(
"CTF: flag_return_touches < 0; this shouldn't happen.")
ba.print_error('CTF flag_return_touches < 0')
def _flash_base(self, team: Team, length: float = 2.0) -> None:
light = ba.newnode('light',
@ -536,13 +538,15 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
self._score_to_win)
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, ba.PlayerDiedMessage):
# Augment standard behavior.
super().handlemessage(msg)
super().handlemessage(msg) # Augment standard behavior.
self.respawn_player(msg.getplayer(Player))
elif isinstance(msg, FlagDiedMessage):
assert isinstance(msg.flag, CTFFlag)
ba.timer(0.1, ba.Call(self._spawn_flag_for_team, msg.flag.team))
elif isinstance(msg, FlagPickedUpMessage):
# Store the last player to hold the flag for scoring purposes.
assert isinstance(msg.flag, CTFFlag)
@ -550,9 +554,11 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
PlayerSpaz, True).getplayer(Player)
msg.flag.held_count += 1
msg.flag.reset_return_times()
elif isinstance(msg, FlagDroppedMessage):
# Store the last player to hold the flag for scoring purposes.
assert isinstance(msg.flag, CTFFlag)
msg.flag.held_count -= 1
else:
super().handlemessage(msg)

View File

@ -177,7 +177,7 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
# If we have a chosen one, ignore these.
if self._get_chosen_one_player() is not None:
return
player = ba.getcollision().opposing_node.getdelegate(
player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player)
if player is not None and player.is_alive():
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:
collision = ba.getcollision()
flag = collision.source_node.getdelegate(ConquestFlag)
player = collision.opposing_node.getdelegate(PlayerSpaz,
True).getplayer(Player)
flag = collision.sourcenode.getdelegate(ConquestFlag)
player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(Player)
if not flag or not player:
return
assert flag.light

View File

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

View File

@ -209,7 +209,7 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
assert self._flag is not None
if self._flag.scored:
return
region = ba.getcollision().source_node
region = ba.getcollision().sourcenode
i = None
for i in range(len(self._score_regions)):
if region == self._score_regions[i].node:
@ -654,7 +654,7 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
return
# See which score region it was.
region = ba.getcollision().source_node
region = ba.getcollision().sourcenode
i = None
for i in range(len(self._score_regions)):
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:
collision = ba.getcollision()
puck = collision.source_node.getdelegate(Puck)
player = collision.opposing_node.getdelegate(PlayerSpaz,
True).getplayer(Player)
puck = collision.sourcenode.getdelegate(Puck)
player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(Player)
if player and puck:
puck.last_players_to_touch[player.team.id] = player
@ -265,7 +265,7 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
if self._puck.scored:
return
region = ba.getcollision().source_node
region = ba.getcollision().sourcenode
index = 0
for index in range(len(self._score_regions)):
if region == self._score_regions[index].node:

View File

@ -247,7 +247,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
ba.playsound(self._swipsound)
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)
if not player:
return

View File

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

View File

@ -406,7 +406,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
ba.timer(2.0, self._start_updating_waves)
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():
return # Ignore bodies flying in.

View File

@ -91,7 +91,7 @@ class ControlsSettingsWindow(ba.Window):
height += space_height
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
height += spacing * 2
show_keyboard_p2 = False if app.vr_mode else show_keyboard
@ -393,7 +393,7 @@ class ControlsSettingsWindow(ba.Window):
self._save_state()
ba.containerwidget(edit=self._root_widget, transition='out_left')
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:
# pylint: disable=cyclic-import
@ -401,7 +401,7 @@ class ControlsSettingsWindow(ba.Window):
self._save_state()
ba.containerwidget(edit=self._root_widget, transition='out_left')
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:
# pylint: disable=cyclic-import

View File

@ -1,5 +1,5 @@
<!-- 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,
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>
@ -189,6 +189,7 @@
<ul>
<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_DelegateNotFoundError">ba.DelegateNotFoundError</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_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>
<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>
<dt><h4><a name="method_ba_App__on_app_pause">on_app_pause()</a></dt></h4><dd>
@ -1298,7 +1299,7 @@ mycall()</pre>
</dd>
</dl>
<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>
<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>
@ -1352,10 +1353,10 @@ mycall()</pre>
<p>Reload all player profiles.</p>
</dd>
<dt><h4><a name="method_ba_Chooser__update_from_player_profiles">update_from_player_profiles()</a></dt></h4><dd>
<p><span>update_from_player_profiles(self) -&gt; None</span></p>
<dt><h4><a name="method_ba_Chooser__update_from_profile">update_from_profile()</a></dt></h4><dd>
<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>
<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>
<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>
<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>The body index on the opposing node in the current collision.</p>
</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>The node the current callback material node is hitting.</p>
@ -1403,7 +1404,7 @@ mycall()</pre>
<p>The position of the current collision.</p>
</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>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>
</ul>
<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>
<p>Inherits from: <a href="#class_typing_Generic">typing.Generic</a></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>
<p><span>delete(ignore_missing: bool = True) -&gt; None</span></p>
<p>Delete the node. Ignores already-deleted nodes unless ignore_missing
is False, in which case an Exception is thrown.</p>
<p>Delete the node. Ignores already-deleted nodes if ignore_missing
is True; otherwise a <a href="#class_ba_NodeNotFoundError">ba.NodeNotFoundError</a> is thrown.</p>
</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
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>
<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>
<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>
<dt><h4><a name="attr_ba_PowerupMessage__poweruptype">poweruptype</a></h4></dt><dd>
<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>
</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>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
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>
</dd>
@ -4114,7 +4125,7 @@ cause the powerup box to make a sound and disappear or whatnot.</p>
<h3>Methods:</h3>
<dl>
<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>
</dl>
@ -4443,7 +4454,7 @@ that a SessionPlayer is still present if retaining references to one
for any length of time.</p>
<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>
<dt><h4><a name="attr_ba_SessionPlayer__character">character</a></h4></dt><dd>
<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
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>
<dt><h4><a name="attr_ba_SessionPlayer__sessiondata">sessiondata</a></h4></dt><dd>
<p><span> Dict</span></p>
@ -4507,7 +4523,7 @@ is still in its lobby selecting a team/etc. then a
</dd>
</dl>
<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>
<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, ...]],
@ -4544,12 +4560,6 @@ joins (while verification occurs).</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>
<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>