Moved shared-objects to bastd

This commit is contained in:
Eric Froemling 2020-05-29 13:59:10 -07:00
parent a44821e03e
commit 4e15d72e4f
34 changed files with 724 additions and 548 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/d9/29/c569224bc159225daed5cabdd517",
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b5/52/a015232b381b5a361e26cc4e33d6",
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b5/31/c229f5293e5ec5b3b8feb9308216",
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3e/8c/2b05b2168897862e0eefc0d5ddaa",
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a6/e5/923be95c40b9e7432f941bb98f79",
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f5/3e/f929b7330662fc64f91e9613d6b3",
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e9/3a/25571131b13d74f19150e8fdf786",
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4f/b3/9627d8ee06297e66f7238fbc4838",
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/fe/45/5646002baebd720592914c7f1c5b",
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/f3/ee/10a2c2eaf9783c9abd81141ee5e8",
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/8e/7b/e121ed5e35abf9cce71415fdecac",
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/16/d7/b53476ad786d0b1636fcf1906578"
"build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8d/c2/9fd3ab19a28b05160f818f787115",
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/67/25/34c42457f20c9d87d538f4f69320",
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/d9/3d/39326060f1a1df1b18070d75ade8",
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/9f/b7/d63ab7e13f1b9d931b83ef652262",
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/39/45/f4d0bbe6dfa0286b1faaad5d4c0e",
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a8/2b/251dcadd37ae0d2c08a0f3bb683d",
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/3a/1a/b80f37d9802d40e625b2df3696b1",
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/46/fd/6400f9eba88a419487f5d3732e54",
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/7d/6a/3dc3c77c340471c0a7b79bf077a4",
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/97/5d/824c5c71f61d871c904c61fabc56",
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/91/92/1f309a3edf4b4aa595ea83472e7a",
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/d5/3c/fefdeed4e8048332724167fe1442"
}

View File

@ -186,6 +186,7 @@
<w>bname</w>
<w>bndl</w>
<w>boffs</w>
<w>bombfactory</w>
<w>bombsquad</w>
<w>bombsquadcb</w>
<w>bombsquadgame</w>
@ -1637,6 +1638,7 @@
<w>sessionclass</w>
<w>sessiondata</w>
<w>sessionglobals</w>
<w>sessionglobalsnode</w>
<w>sessionname</w>
<w>sessionplayer</w>
<w>sessionteam</w>
@ -1655,6 +1657,7 @@
<w>sharedobjs</w>
<w>shiftdelay</w>
<w>shiftposition</w>
<w>shobs</w>
<w>shortname</w>
<w>shouldn</w>
<w>showpoints</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=237466057120267570582079835997969754357
# SOURCES_HASH=214732868903831008614942145147141302125
# I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression
@ -527,7 +527,7 @@ class Material:
# example 3: play some sounds when we're contacting the ground:
m = ba.Material()
m.add_actions(conditions=('they_have_material',
ba.sharedobj('footing_material')),
shared.footing_material),
actions=(('impact_sound', ba.getsound('metalHit'), 2, 5),
('skid_sound', ba.getsound('metalSkid'), 2, 5)))
@ -635,6 +635,10 @@ class Node:
move_left_right: float = 0.0
curse_death_time: int = 0
boxing_gloves: bool = False
use_fixed_vr_overlay: bool = False
allow_kick_idle_players: bool = False
music_continuous: bool = False
music_count: int = 0
hurt: float = 0.0
always_show_health_bar: bool = False
mini_billboard_1_texture: Optional[ba.Texture] = None
@ -648,7 +652,20 @@ class Node:
mini_billboard_3_end_time: int = 0
boxing_gloves_flashing: bool = False
dead: bool = False
floor_reflection: bool = False
debris_friction: float = 0.0
debris_kill_height: float = 0.0
vr_near_clip: float = 0.0
shadow_ortho: bool = False
happy_thoughts_mode: bool = False
shadow_offset: Sequence[float] = (0.0, 0.0)
paused: bool = False
time: int = 0
ambient_color: Sequence[float] = (1.0, 1.0, 1.0)
camera_mode: str = 'rotate'
frozen: bool = False
area_of_interest_bounds: Sequence[float] = (-1, -1, -1, 1, 1, 1)
shadow_range: Sequence[float] = (0, 0, 0, 0)
counter_text: str = ''
counter_texture: Optional[ba.Texture] = None
shattered: int = 0

View File

@ -39,7 +39,8 @@ from _ba import (CollideModel, Context, ContextCall, Data, InputDevice,
open_url, widget)
from ba._activity import Activity
from ba._actor import Actor
from ba._player import PlayerInfo, Player, playercast, playercast_o
from ba._player import (PlayerInfo, Player, playercast, playercast_o,
StandLocation)
from ba._nodeactor import NodeActor
from ba._app import App
from ba._coopgame import CoopGameActivity
@ -71,7 +72,7 @@ from ba._appdelegate import AppDelegate
from ba._apputils import is_browser_likely_available
from ba._campaign import Campaign
from ba._gameutils import (animate, animate_array, show_damage_count,
sharedobj, timestring, cameraflash)
timestring, cameraflash)
from ba._general import (WeakCall, Call, existing, Existable,
verify_object_death)
from ba._level import Level

View File

@ -26,7 +26,8 @@ from typing import TYPE_CHECKING, Generic, TypeVar
from ba._team import Team
from ba._player import Player
from ba._error import print_exception, print_error, SessionTeamNotFoundError
from ba._error import (print_exception, SessionTeamNotFoundError,
NodeNotFoundError)
from ba._dependency import DependencyComponent
from ba._general import Call, verify_object_death
from ba._messages import UNHANDLED
@ -155,6 +156,11 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# Create our internal engine data.
self._activity_data = _ba.register_activity(self)
assert isinstance(settings, dict)
assert _ba.getactivity() is self
self._globalsnode: Optional[ba.Node] = None
# Player/Team types should have been specified as type args;
# grab those.
self._playertype: Type[PlayerType]
@ -174,11 +180,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# Preloaded data for actors, maps, etc; indexed by type.
self.preloads: Dict[Type, Any] = {}
if not isinstance(settings, dict):
raise TypeError('expected dict for settings')
if _ba.getactivity(doraise=False) is not self:
raise Exception('invalid context state')
# Hopefully can eventually kill this; activities should
# validate/store whatever settings they need at init time
# (in a more type-safe way).
@ -191,9 +192,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
self._should_end_immediately_results: (
Optional[ba.TeamGameResults]) = None
self._should_end_immediately_delay = 0.0
self._called_activity_on_transition_in = False
self._called_activity_on_begin = False
self._activity_death_check_timer: Optional[ba.Timer] = None
self._expired = False
@ -234,6 +232,16 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
Call(session.transitioning_out_activity_was_freed,
self.can_show_ad_on_death))
@property
def globalsnode(self) -> ba.Node:
"""The 'globals' ba.Node for the activity. This contains various
global controls and values.
"""
node = self._globalsnode
if not node:
raise NodeNotFoundError()
return node
@property
def stats(self) -> ba.Stats:
"""The stats instance accessible while the activity is running.
@ -334,12 +342,11 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
from ba import _actor as bsactor
if not isinstance(actor, bsactor.Actor):
raise TypeError('non-actor passed to retain_actor')
if (self.has_transitioned_in()
and _ba.time() - self._last_prune_dead_actors_time > 10.0):
print_error('It looks like nodes/actors are not'
' being pruned in your activity;'
' did you call Activity.on_transition_in()'
' from your subclass?; ' + str(self) + ' (loc. a)')
# Make sure our pruning is happening...
assert (not self.has_transitioned_in()
or _ba.time() - self._last_prune_dead_actors_time < 10.0)
self._actor_refs.append(actor)
def add_actor_weak_ref(self, actor: ba.Actor) -> None:
@ -350,12 +357,11 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
from ba import _actor as bsactor
if not isinstance(actor, bsactor.Actor):
raise TypeError('non-actor passed to add_actor_weak_ref')
if (self.has_transitioned_in()
and _ba.time() - self._last_prune_dead_actors_time > 10.0):
print_error('It looks like nodes/actors are '
'not being pruned in your activity;'
' did you call Activity.on_transition_in()'
' from your subclass?; ' + str(self) + ' (loc. b)')
# Make sure our pruning is happening...
assert (not self.has_transitioned_in()
or _ba.time() - self._last_prune_dead_actors_time < 10.0)
self._actor_weak_refs.append(weakref.ref(actor))
@property
@ -396,7 +402,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
or teams, however. They remain owned by the previous Activity
up until ba.Activity.on_begin() is called.
"""
self._called_activity_on_transition_in = True
def on_transition_out(self) -> None:
"""Called when your activity begins transitioning out.
@ -411,28 +416,12 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
At this point the activity's initial players and teams are filled in
and it should begin its actual game logic.
"""
self._called_activity_on_begin = True
def handlemessage(self, msg: Any) -> Any:
"""General message handling; can be passed any message object."""
del msg # Unused arg.
return UNHANDLED
def end(self,
results: Any = None,
delay: float = 0.0,
force: bool = False) -> None:
"""Commences Activity shutdown and delivers results to the ba.Session.
'delay' is the time delay before the Activity actually ends
(in seconds). Further calls to end() will be ignored up until
this time, unless 'force' is True, in which case the new results
will replace the old.
"""
# Ask the session to end us.
self.session.end_activity(self, results, delay, force)
def has_transitioned_in(self) -> bool:
"""Return whether on_transition_in() has been called."""
return self._has_transitioned_in
@ -455,15 +444,15 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
(internal)
"""
from ba._general import WeakCall
from ba._gameutils import sharedobj
assert not self._has_transitioned_in
self._has_transitioned_in = True
# Set up the globals node based on our settings.
with _ba.Context(self):
glb = self._globalsnode = _ba.newnode('globals')
# Now that it's going to be front and center,
# set some global values based on what the activity wants.
glb = sharedobj('globals')
glb.use_fixed_vr_overlay = self.use_fixed_vr_overlay
glb.allow_kick_idle_players = self.allow_kick_idle_players
if self.inherits_slow_motion and prev_globals is not None:
@ -498,7 +487,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
try:
self.on_transition_in()
except Exception:
print_exception('Error in on_transition_in for', self)
print_exception(f'Error in on_transition_in for {self}')
# Tell the C++ layer that this activity is the main one, so it uses
# settings from our globals, directs various events to us, etc.
@ -538,8 +527,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
self._has_begun = True
self.on_begin()
self._sanity_check_begin_call()
# If the whole session wants to die and was waiting on us,
# can kick off that process now.
if session.wants_to_end:
@ -550,6 +537,21 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
self.end(self._should_end_immediately_results,
self._should_end_immediately_delay)
def end(self,
results: Any = None,
delay: float = 0.0,
force: bool = False) -> None:
"""Commences Activity shutdown and delivers results to the ba.Session.
'delay' is the time delay before the Activity actually ends
(in seconds). Further calls to end() will be ignored up until
this time, unless 'force' is True, in which case the new results
will replace the old.
"""
# Ask the session to end us.
self.session.end_activity(self, results, delay, force)
def create_player(self, sessionplayer: ba.SessionPlayer) -> PlayerType:
"""Create the Player instance for this Activity.
@ -559,7 +561,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
point as it is not yet fully wired up; wait for on_player_join()
for that.
"""
del sessionplayer # Unused
del sessionplayer # Unused.
player = self._playertype()
return player
@ -686,19 +688,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
sessionteam.gameteam = None
def _sanity_check_begin_call(self) -> None:
# Make sure ba.Activity.on_transition_in() got called at some point.
if not self._called_activity_on_transition_in:
print_error(
'ba.Activity.on_transition_in() never got called for ' +
str(self) + '; did you forget to call it'
' in your on_transition_in override?')
# Make sure that ba.Activity.on_begin() got called at some point.
if not self._called_activity_on_begin:
print_error(
'ba.Activity.on_begin() never got called for ' + str(self) +
'; did you forget to call it in your on_begin override?')
def _setup_player_and_team_types(self) -> None:
"""Pull player and team types from our typing.Generic params."""
@ -819,7 +808,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# It is expected that Team objects may last longer than
# the SessionTeam they came from (game objects may hold
# team references past the point at which the underlying
# player/team leaves)
# player/team has left the game)
pass
except Exception:
print_exception(f'Error resetting Team {team}')

View File

@ -623,7 +623,7 @@ class App:
# FIXME: Shouldn't be touching scene stuff here;
# should just pass the request on to the host-session.
with _ba.Context(activity):
globs = _gameutils.sharedobj('globals')
globs = activity.globalsnode
if not globs.paused:
_ba.playsound(_ba.getsound('refWhistle'))
globs.paused = True
@ -645,14 +645,13 @@ class App:
If there's a foreground host-activity that's currently paused, tell it
to resume.
"""
from ba._gameutils import sharedobj
# FIXME: Shouldn't be touching scene stuff here;
# should just pass the request on to the host-session.
activity = _ba.get_foreground_host_activity()
if activity is not None:
with _ba.Context(activity):
globs = sharedobj('globals')
globs = activity.globalsnode
if globs.paused:
_ba.playsound(_ba.getsound('refWhistle'))
globs.paused = False

View File

@ -249,7 +249,7 @@ class CoopGameActivity(GameActivity[PlayerType, TeamType]):
def fade_to_red(self) -> None:
"""Fade the screen to red; (such as when the good guys have lost)."""
from ba import _gameutils
c_existing = _gameutils.sharedobj('globals').tint
c_existing = self.globalsnode.tint
cnode = _ba.newnode('combine',
attrs={
'input0': c_existing[0],
@ -259,7 +259,7 @@ class CoopGameActivity(GameActivity[PlayerType, TeamType]):
})
_gameutils.animate(cnode, 'input1', {0: c_existing[1], 2.0: 0})
_gameutils.animate(cnode, 'input2', {0: c_existing[2], 2.0: 0})
cnode.connectattr('output', _gameutils.sharedobj('globals'), 'tint')
cnode.connectattr('output', self.globalsnode, 'tint')
def setup_low_life_warning_sound(self) -> None:
"""Set up a beeping noise to play when any players are near death."""

View File

@ -403,6 +403,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
return ''
def on_transition_in(self) -> None:
super().on_transition_in()
# Make our map.
@ -455,7 +456,6 @@ class GameActivity(Activity[PlayerType, TeamType]):
# pylint: disable=too-many-nested-blocks
# pylint: disable=cyclic-import
from bastd.ui.continues import ContinuesWindow
from ba._gameutils import sharedobj
from ba._coopsession import CoopSession
from ba._enums import TimeType
@ -472,7 +472,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
if isinstance(session, CoopSession):
assert session.campaign is not None
if session.campaign.sequential:
gnode = sharedobj('globals')
gnode = self.globalsnode
# Only attempt this if we're not currently paused
# and there appears to be no UI.
@ -1024,7 +1024,6 @@ class GameActivity(Activity[PlayerType, TeamType]):
This will be displayed at the top of the screen.
If the time-limit expires, end_game() will be called.
"""
from ba._gameutils import sharedobj
from ba._nodeactor import NodeActor
if duration <= 0.0:
return
@ -1048,8 +1047,9 @@ class GameActivity(Activity[PlayerType, TeamType]):
'time2': duration * 1000,
'timemin': 0
}))
sharedobj('globals').connectattr(
'time', self._standard_time_limit_text_input.node, 'time1')
self.globalsnode.connectattr('time',
self._standard_time_limit_text_input.node,
'time1')
assert self._standard_time_limit_text_input.node
assert self._standard_time_limit_text.node
self._standard_time_limit_text_input.node.connectattr(

View File

@ -23,10 +23,10 @@
from __future__ import annotations
from typing import TYPE_CHECKING
# from typing_extensions import Protocol
import _ba
from ba._enums import TimeType, TimeFormat, SpecialChar
from ba._error import ActivityNotFoundError
if TYPE_CHECKING:
from typing import Any, Dict, Sequence, Optional
@ -41,15 +41,6 @@ TROPHY_CHARS = {
'4': SpecialChar.TROPHY4
}
# class Respawnable(Protocol):
# """A Protocol for objects able to be respawned.
# Category: Protocols
# """
# respawn_timer: Optional[ba.Timer]
# respawn_icon: Optional[RespawnIcon]
def get_trophy_string(trophy_id: str) -> str:
"""Given a trophy id, returns a string to visualize it."""
@ -58,125 +49,6 @@ def get_trophy_string(trophy_id: str) -> str:
return '?'
def sharedobj(name: str) -> Any:
"""Return a predefined object for the current Activity, creating if needed.
Category: Gameplay Functions
Available values for 'name':
'globals': returns the 'globals' ba.Node, containing various global
controls & values.
'object_material': a ba.Material that should be applied to any small,
normal, physical objects such as bombs, boxes, players, etc. Other
materials often check for the presence of this material as a
prerequisite for performing certain actions (such as disabling collisions
between initially-overlapping objects)
'player_material': a ba.Material to be applied to player parts. Generally,
materials related to the process of scoring when reaching a goal, etc
will look for the presence of this material on things that hit them.
'pickup_material': a ba.Material; collision shapes used for picking things
up will have this material applied. To prevent an object from being
picked up, you can add a material that disables collisions against things
containing this material.
'footing_material': anything that can be 'walked on' should have this
ba.Material applied; generally just terrain and whatnot. A character will
snap upright whenever touching something with this material so it should
not be applied to props, etc.
'attack_material': a ba.Material applied to explosion shapes, punch
shapes, etc. An object not wanting to receive impulse/etc messages can
disable collisions against this material.
'death_material': a ba.Material that sends a ba.DieMessage() to anything
that touches it; handy for terrain below a cliff, etc.
'region_material': a ba.Material used for non-physical collision shapes
(regions); collisions can generally be allowed with this material even
when initially overlapping since it is not physical.
'railing_material': a ba.Material with a very low friction/stiffness/etc
that can be applied to invisible 'railings' useful for gently keeping
characters from falling off of cliffs.
"""
# pylint: disable=too-many-branches
from ba._messages import DieMessage
# We store these on the current context; whether its an activity or
# session.
activity = _ba.getactivity(doraise=False)
if activity is not None:
# Grab shared-objs dict.
sharedobjs = getattr(activity, 'sharedobjs', None)
# Grab item out of it.
try:
return sharedobjs[name] # (pylint bug?) pylint: disable=E1136
except KeyError:
pass
obj: Any
# Hmm looks like it doesn't yet exist; create it if its a valid value.
if name == 'globals':
node_obj = _ba.newnode('globals')
obj = node_obj
elif name in [
'object_material', 'player_material', 'pickup_material',
'footing_material', 'attack_material'
]:
obj = _ba.Material()
elif name == 'death_material':
mat = obj = _ba.Material()
mat.add_actions(
('message', 'their_node', 'at_connect', DieMessage()))
elif name == 'region_material':
obj = _ba.Material()
elif name == 'railing_material':
mat = obj = _ba.Material()
mat.add_actions(('modify_part_collision', 'collide', False))
mat.add_actions(('modify_part_collision', 'stiffness', 0.003))
mat.add_actions(('modify_part_collision', 'damping', 0.00001))
mat.add_actions(conditions=('they_have_material',
sharedobj('player_material')),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision',
'friction', 0.0)))
else:
raise ValueError(
"unrecognized shared object (activity context): '" + name +
"'")
else:
session = _ba.getsession(doraise=False)
if session is not None:
# Grab shared-objs dict (creating if necessary).
sharedobjs = session.sharedobjs
# Grab item out of it.
obj = sharedobjs.get(name)
if obj is not None:
return obj
# Hmm looks like it doesn't yet exist; create if its a valid value.
if name == 'globals':
obj = _ba.newnode('sessionglobals')
else:
raise ValueError('unrecognized shared object '
"(session context): '" + name + "'")
else:
raise RuntimeError('no current activity or session context')
# Ok, got a shiny new shared obj; store it for quick access next time.
sharedobjs[name] = obj
return obj
def animate(node: ba.Node,
attr: str,
keys: Dict[float, float],
@ -238,7 +110,14 @@ def animate(node: ba.Node,
# Do the connects last so all our attrs are in place when we push initial
# values through.
sharedobj('globals').connectattr(driver, curve, 'in')
# We operate in either activities or sessions..
try:
globalsnode = _ba.getactivity().globalsnode
except ActivityNotFoundError:
globalsnode = _ba.getsession().sessionglobalsnode
globalsnode.connectattr(driver, curve, 'in')
curve.connectattr('out', node, attr)
return curve
@ -282,12 +161,18 @@ def animate_array(node: ba.Node,
else:
raise ValueError('invalid timeformat value: "' + str(timeformat) + '"')
# We operate in either activities or sessions..
try:
globalsnode = _ba.getactivity().globalsnode
except ActivityNotFoundError:
globalsnode = _ba.getsession().sessionglobalsnode
for i in range(size):
curve = _ba.newnode('animcurve',
owner=node,
name=('Driving ' + str(node) + ' \'' + attr +
'\' member ' + str(i)))
sharedobj('globals').connectattr(driver, curve, 'in')
globalsnode.connectattr(driver, curve, 'in')
curve.times = [int(mult * time) for time, val in items]
curve.values = [val[i] for time, val in items]
curve.offset = _ba.time(timeformat=TimeFormat.MILLISECONDS) + int(

View File

@ -226,7 +226,7 @@ class Map(Actor):
' staticmethod in the activity constructor')
# Set various globals.
gnode = _gameutils.sharedobj('globals')
gnode = _ba.getactivity().globalsnode
# Set area-of-interest bounds.
aoi_bounds = self.get_def_bound_box('area_of_interest_bounds')

View File

@ -506,7 +506,7 @@ def setmusic(musictype: Optional[MusicType], continuous: bool = False) -> None:
# the do_play_music call in our music controller. This way we can
# seamlessly support custom soundtracks in replays/etc since we're being
# driven purely by node data.
gnode = _gameutils.sharedobj('globals')
gnode = _ba.getactivity().globalsnode
gnode.music_continuous = continuous
gnode.music = '' if musictype is None else musictype.value
gnode.music_count += 1

View File

@ -46,6 +46,16 @@ class PlayerInfo:
character: str
@dataclass
class StandLocation:
"""Describes a point in space and an angle to face.
Category: Gameplay Classes
"""
position: _ba.Vec3
angle: Optional[float] = None
class Player(Generic[TeamType]):
"""A player in a specific ba.Activity.

View File

@ -25,7 +25,7 @@ import weakref
from typing import TYPE_CHECKING
import _ba
from ba._error import print_error, print_exception
from ba._error import print_error, print_exception, NodeNotFoundError
from ba._lang import Lstr
from ba._player import Player
@ -113,7 +113,6 @@ class Session:
# pylint: disable=cyclic-import
from ba._lobby import Lobby
from ba._stats import Stats
from ba._gameutils import sharedobj
from ba._gameactivity import GameActivity
from ba._activity import Activity
from ba._team import SessionTeam
@ -201,7 +200,15 @@ class Session:
self.stats = Stats()
# Instantiate our session globals node which will apply its settings.
sharedobj('globals')
self._sessionglobalsnode = _ba.newnode('sessionglobals')
@property
def sessionglobalsnode(self) -> ba.Node:
"""The sessionglobals ba.Node for the session."""
node = self._sessionglobalsnode
if not node:
raise NodeNotFoundError()
return node
def on_player_request(self, player: ba.SessionPlayer) -> bool:
"""Called when a new ba.Player wants to join the Session.
@ -347,16 +354,6 @@ class Session:
def on_team_leave(self, team: ba.SessionTeam) -> None:
"""Called when a ba.Team is leaving the session."""
def _complete_end_activity(self, activity: ba.Activity,
results: Any) -> None:
# Run the subclass callback in the session context.
try:
with _ba.Context(self):
self.on_activity_end(activity, results)
except Exception:
print_exception('exception in on_activity_end() for session', self,
'activity', activity, 'with results', results)
def end_activity(self, activity: ba.Activity, results: Any, delay: float,
force: bool) -> None:
"""Commence shutdown of a ba.Activity (if not already occurring).
@ -414,7 +411,6 @@ class Session:
(on_transition_in, etc) to get it. (so you can't do
session.set_activity(foo) and then ba.newnode() to add a node to foo)
"""
from ba._gameutils import sharedobj
from ba._enums import TimeType
# Sanity test: make sure this doesn't get called recursively.
@ -439,11 +435,8 @@ class Session:
str(self._next_activity) + ')')
prev_activity = self._activity_retained
if prev_activity is not None:
with _ba.Context(prev_activity):
prev_globals = sharedobj('globals')
else:
prev_globals = None
prev_globals = (prev_activity.globalsnode
if prev_activity is not None else None)
# Let the activity do its thing.
activity.transition_in(prev_globals)
@ -491,6 +484,16 @@ class Session:
"""
return []
def _complete_end_activity(self, activity: ba.Activity,
results: Any) -> None:
# Run the subclass callback in the session context.
try:
with _ba.Context(self):
self.on_activity_end(activity, results)
except Exception:
print_exception('exception in on_activity_end() for session', self,
'activity', activity, 'with results', results)
def _request_player(self, sessionplayer: ba.SessionPlayer) -> bool:
"""Called by the C++ layer when players want to join."""

View File

@ -26,12 +26,19 @@
from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, TypeVar
import ba
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence, Optional, Callable, List, Tuple
from typing import Any, Sequence, Optional, Callable, List, Tuple, Type
# Attr we store these objects as on the current activity.
# (based on our module so hopefully avoids conflicts)
STORAGE_ATTR_NAME = '_' + __name__.replace('.', '_') + '_bombfactory'
PlayerType = TypeVar('PlayerType', bound='ba.Player')
class BombFactory:
@ -153,6 +160,7 @@ class BombFactory:
You shouldn't need to do this; call bastd.actor.bomb.get_factory()
to get a shared instance.
"""
shared = SharedObjects.get()
self.bomb_model = ba.getmodel('bomb')
self.sticky_bomb_model = ba.getmodel('bombSticky')
@ -191,17 +199,24 @@ class BombFactory:
self.sticky_material = ba.Material()
self.bomb_material.add_actions(
conditions=((('we_are_younger_than', 100), 'or',
('they_are_younger_than', 100)),
'and', ('they_have_material',
ba.sharedobj('object_material'))),
actions=('modify_node_collision', 'collide', False))
conditions=(
(
('we_are_younger_than', 100),
'or',
('they_are_younger_than', 100),
),
'and',
('they_have_material', shared.object_material),
),
actions=('modify_node_collision', 'collide', False),
)
# we want pickup materials to always hit us even if we're currently not
# colliding with their node (generally due to the above rule)
self.bomb_material.add_actions(
conditions=('they_have_material', ba.sharedobj('pickup_material')),
actions=('modify_part_collision', 'use_node_collide', False))
self.bomb_material.add_actions(conditions=('they_have_material',
shared.pickup_material),
actions=('modify_part_collision',
'use_node_collide', False))
self.bomb_material.add_actions(actions=('modify_part_collision',
'friction', 0.3))
@ -209,36 +224,54 @@ class BombFactory:
self.land_mine_no_explode_material = ba.Material()
self.land_mine_blast_material = ba.Material()
self.land_mine_blast_material.add_actions(
conditions=(('we_are_older_than',
200), 'and', ('they_are_older_than',
200), 'and', ('eval_colliding', ),
'and', (('they_dont_have_material',
self.land_mine_no_explode_material), 'and',
(('they_have_material',
ba.sharedobj('object_material')), 'or',
('they_have_material',
ba.sharedobj('player_material'))))),
actions=('message', 'our_node', 'at_connect', ImpactMessage()))
conditions=(
('we_are_older_than', 200),
'and',
('they_are_older_than', 200),
'and',
('eval_colliding', ),
'and',
(
('they_dont_have_material',
self.land_mine_no_explode_material),
'and',
(
('they_have_material', shared.object_material),
'or',
('they_have_material', shared.player_material),
),
),
),
actions=('message', 'our_node', 'at_connect', ImpactMessage()),
)
self.impact_blast_material = ba.Material()
self.impact_blast_material.add_actions(
conditions=(('we_are_older_than',
200), 'and', ('they_are_older_than',
200), 'and', ('eval_colliding', ),
'and', (('they_have_material',
ba.sharedobj('footing_material')), 'or',
('they_have_material',
ba.sharedobj('object_material')))),
actions=('message', 'our_node', 'at_connect', ImpactMessage()))
conditions=(
('we_are_older_than', 200),
'and',
('they_are_older_than', 200),
'and',
('eval_colliding', ),
'and',
(
('they_have_material', shared.footing_material),
'or',
('they_have_material', shared.object_material),
),
),
actions=('message', 'our_node', 'at_connect', ImpactMessage()),
)
self.blast_material = ba.Material()
self.blast_material.add_actions(
conditions=(('they_have_material',
ba.sharedobj('object_material'))),
actions=(('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical',
False), ('message', 'our_node', 'at_connect',
ExplodeHitMessage())))
conditions=(('they_have_material', shared.object_material), ),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect', ExplodeHitMessage()),
),
)
self.dink_sounds = (ba.getsound('bombDrop01'),
ba.getsound('bombDrop02'))
@ -247,10 +280,11 @@ class BombFactory:
# collision sounds
self.normal_sound_material.add_actions(
conditions=('they_have_material',
ba.sharedobj('footing_material')),
actions=(('impact_sound', self.dink_sounds, 2, 0.8),
('roll_sound', self.roll_sound, 3, 6)))
conditions=('they_have_material', shared.footing_material),
actions=(
('impact_sound', self.dink_sounds, 2, 0.8),
('roll_sound', self.roll_sound, 3, 6),
))
self.sticky_material.add_actions(actions=(('modify_part_collision',
'stiffness', 0.1),
@ -258,24 +292,22 @@ class BombFactory:
'damping', 1.0)))
self.sticky_material.add_actions(
conditions=(('they_have_material',
ba.sharedobj('player_material')),
'or', ('they_have_material',
ba.sharedobj('footing_material'))),
actions=('message', 'our_node', 'at_connect', SplatMessage()))
conditions=(
('they_have_material', shared.player_material),
'or',
('they_have_material', shared.footing_material),
),
actions=('message', 'our_node', 'at_connect', SplatMessage()),
)
def get_factory() -> BombFactory:
"""Get/create a shared bastd.actor.bomb.BombFactory object."""
activity = ba.getactivity()
# FIXME: Need to figure out an elegant way to store
# shared actor data with an activity.
factory: BombFactory
try:
factory = activity.shared_bomb_factory # type: ignore
except Exception:
factory = activity.shared_bomb_factory = BombFactory() # type: ignore
factory = getattr(activity, STORAGE_ATTR_NAME, None)
if factory is None:
factory = BombFactory()
setattr(activity, STORAGE_ATTR_NAME, factory)
assert isinstance(factory, BombFactory)
return factory
@ -329,26 +361,27 @@ class Blast(ba.Actor):
super().__init__()
shared = SharedObjects.get()
factory = get_factory()
self.blast_type = blast_type
self.source_player = source_player
self._source_player = source_player
self.hit_type = hit_type
self.hit_subtype = hit_subtype
self.radius = blast_radius
# set our position a bit lower so we throw more things upward
rmats = (factory.blast_material, ba.sharedobj('attack_material'))
self.node = ba.newnode('region',
delegate=self,
attrs={
'position': (position[0], position[1] - 0.1,
position[2]),
'scale':
(self.radius, self.radius, self.radius),
'type': 'sphere',
'materials': rmats
})
rmats = (factory.blast_material, shared.attack_material)
self.node = ba.newnode(
'region',
delegate=self,
attrs={
'position': (position[0], position[1] - 0.1, position[2]),
'scale': (self.radius, self.radius, self.radius),
'type': 'sphere',
'materials': rmats
},
)
ba.timer(0.05, self.node.delete)
@ -619,7 +652,7 @@ class Blast(ba.Actor):
hit_type=self.hit_type,
hit_subtype=self.hit_subtype,
radius=self.radius,
source_player=ba.existing(self.source_player)))
source_player=ba.existing(self._source_player)))
if self.blast_type == 'ice':
ba.playsound(get_factory().freeze_sound, 10, position=nodepos)
node.handlemessage(ba.FreezeMessage())
@ -654,6 +687,7 @@ class Bomb(ba.Actor):
"""
super().__init__()
shared = SharedObjects.get()
factory = get_factory()
if bomb_type not in ('ice', 'impact', 'land_mine', 'normal', 'sticky',
@ -681,7 +715,7 @@ class Bomb(ba.Actor):
self._explode_callbacks: List[Callable[[Bomb, Blast], Any]] = []
# the player this came from
self.source_player = source_player
self._source_player = source_player
# by default our hit type/subtype is our own, but we pick up types of
# whoever sets us off so we know what caused a chain reaction
@ -702,12 +736,10 @@ class Bomb(ba.Actor):
# ground.. perhaps we don't wanna add this even in the tnt case?..
materials: Tuple[ba.Material, ...]
if self.bomb_type == 'tnt':
materials = (factory.bomb_material,
ba.sharedobj('footing_material'),
ba.sharedobj('object_material'))
materials = (factory.bomb_material, shared.footing_material,
shared.object_material)
else:
materials = (factory.bomb_material,
ba.sharedobj('object_material'))
materials = (factory.bomb_material, shared.object_material)
if self.bomb_type == 'impact':
materials = materials + (factory.impact_blast_material, )
@ -824,11 +856,20 @@ class Bomb(ba.Actor):
ba.animate(self.node, 'model_scale', {0: 0, 0.2: 1.3, 0.26: 1})
def get_source_player(self) -> Optional[ba.Player]:
"""Returns a ba.Player representing the source of this bomb.
def get_source_player(
self, playertype: Type[PlayerType]) -> Optional[PlayerType]:
"""Return the source-player if there is one and they still exist.
Be prepared for values of None or invalid Player refs."""
return self.source_player
The type of player for the current activity should be passed so that
the type-checker properly identifies the returned value as one.
"""
player: Any = self._source_player
assert isinstance(player, (playertype, type(None)))
# We should not be delivering invalid refs.
# (technically if someone holds on to this message this can happen)
assert player is None or player.exists()
return player
def on_expire(self) -> None:
super().on_expire()
@ -899,7 +940,7 @@ class Bomb(ba.Actor):
velocity=self.node.velocity,
blast_radius=self.blast_radius,
blast_type=self.bomb_type,
source_player=self.source_player,
source_player=ba.existing(self._source_player),
hit_type=self.hit_type,
hit_subtype=self.hit_subtype).autoretain()
for callback in self._explode_callbacks:
@ -979,7 +1020,7 @@ class Bomb(ba.Actor):
# person causing them).
source_player = msg.get_source_player(ba.Player)
if source_player is not None:
self.source_player = source_player
self._source_player = source_player
# Also inherit the hit type (if a landmine sets off by a bomb,
# the credit should go to the mine)
@ -1007,12 +1048,14 @@ class Bomb(ba.Actor):
self.explode()
elif isinstance(msg, ImpactMessage):
self._handle_impact()
elif isinstance(msg, ba.PickedUpMessage):
# change our source to whoever just picked us up *only* if its None
# this way we can get points for killing bots with their own bombs
# hmm would there be a downside to this?...
if self.source_player is not None:
self.source_player = msg.node.source_player
# Ok the logic below looks like it was backwards to me. Disabling
# until further notice.
# elif isinstance(msg, ba.PickedUpMessage):
# # Change our source to whoever just picked us up *only* if it
# # is None. This way we can get points for killing bots with their
# # own bombs. Hmm would there be a downside to this?
# if self._source_player is not None:
# self._source_player = msg.node.source_player
elif isinstance(msg, SplatMessage):
self._handle_splat()
elif isinstance(msg, ba.DroppedMessage):

View File

@ -26,6 +26,7 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING
import ba
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence, Optional
@ -64,13 +65,13 @@ class FlagFactory:
You shouldn't need to do this; call bastd.actor.flag.get_factory() to
get a shared instance.
"""
shared = SharedObjects.get()
self.flagmaterial = ba.Material()
self.flagmaterial.add_actions(
conditions=(
('we_are_younger_than', 100),
'and',
('they_have_material', ba.sharedobj('object_material')),
('they_have_material', shared.object_material),
),
actions=('modify_node_collision', 'collide', False),
)
@ -78,7 +79,7 @@ class FlagFactory:
self.flagmaterial.add_actions(
conditions=(
'they_have_material',
ba.sharedobj('footing_material'),
shared.footing_material,
),
actions=(
('message', 'our_node', 'at_connect', 'footing', 1),
@ -91,7 +92,7 @@ class FlagFactory:
self.flagmaterial.add_actions(
conditions=(
'they_have_material',
ba.sharedobj('footing_material'),
shared.footing_material,
),
actions=(
('impact_sound', self.impact_sound, 2, 5),
@ -102,9 +103,9 @@ class FlagFactory:
self.no_hit_material = ba.Material()
self.no_hit_material.add_actions(
conditions=(
('they_have_material', ba.sharedobj('pickup_material')),
('they_have_material', shared.pickup_material),
'or',
('they_have_material', ba.sharedobj('attack_material')),
('they_have_material', shared.attack_material),
),
actions=('modify_part_collision', 'collide', False),
)
@ -112,9 +113,9 @@ class FlagFactory:
# We also don't want anything moving it.
self.no_hit_material.add_actions(
conditions=(
('they_have_material', ba.sharedobj('object_material')),
('they_have_material', shared.object_material),
'or',
('they_dont_have_material', ba.sharedobj('footing_material')),
('they_dont_have_material', shared.footing_material),
),
actions=(('modify_part_collision', 'collide', False),
('modify_part_collision', 'physical', False)),
@ -215,6 +216,7 @@ class Flag(ba.Actor):
self._initial_position: Optional[Sequence[float]] = None
self._has_moved = False
shared = SharedObjects.get()
factory = FlagFactory.get()
if materials is None:
@ -225,9 +227,8 @@ class Flag(ba.Actor):
if not touchable:
materials = [factory.no_hit_material] + materials
finalmaterials = (
[ba.sharedobj('object_material'), factory.flagmaterial] +
materials)
finalmaterials = ([shared.object_material, factory.flagmaterial] +
materials)
self.node = ba.newnode('flag',
attrs={
'position':

View File

@ -79,7 +79,7 @@ class OnScreenCountdown(ba.Actor):
def start(self) -> None:
"""Start the timer."""
globalsnode = ba.sharedobj('globals')
globalsnode = ba.getactivity().globalsnode
globalsnode.connectattr('time', self.inputnode, 'time1')
self.inputnode.time2 = (globalsnode.time +
(self._timeremaining + 1) * 1000)
@ -87,7 +87,8 @@ class OnScreenCountdown(ba.Actor):
def on_expire(self) -> None:
super().on_expire()
# release callbacks/refs
# Release callbacks/refs.
self._endcall = None
def _update(self, forcevalue: int = None) -> None:

View File

@ -66,7 +66,8 @@ class OnScreenTimer(ba.Actor):
assert isinstance(tval, int)
self._starttime_ms = tval
self.inputnode.time1 = self._starttime_ms
ba.sharedobj('globals').connectattr('time', self.inputnode, 'time2')
ba.getactivity().globalsnode.connectattr('time', self.inputnode,
'time2')
def has_started(self) -> bool:
"""Return whether this timer has started yet."""

View File

@ -26,6 +26,7 @@ import random
from typing import TYPE_CHECKING
import ba
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import List, Any, Optional, Sequence
@ -104,6 +105,7 @@ class PowerupBoxFactory:
to get a shared instance.
"""
from ba.internal import get_default_powerup_distribution
shared = SharedObjects.get()
self._lastpoweruptype: Optional[str] = None
self.model = ba.getmodel('powerup')
self.model_simple = ba.getmodel('powerupSimple')
@ -130,19 +132,22 @@ class PowerupBoxFactory:
# Pass a powerup-touched message to applicable stuff.
self.powerup_material.add_actions(
conditions=('they_have_material', self.powerup_accept_material),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect', _TouchedMessage())))
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect', _TouchedMessage()),
))
# We don't wanna be picked up.
self.powerup_material.add_actions(
conditions=('they_have_material', ba.sharedobj('pickup_material')),
actions=('modify_part_collision', 'collide', False))
conditions=('they_have_material', shared.pickup_material),
actions=('modify_part_collision', 'collide', False),
)
self.powerup_material.add_actions(
conditions=('they_have_material',
ba.sharedobj('footing_material')),
actions=('impact_sound', self.drop_sound, 0.5, 0.1))
conditions=('they_have_material', shared.footing_material),
actions=('impact_sound', self.drop_sound, 0.5, 0.1),
)
self._powerupdist: List[str] = []
for powerup, freq in get_default_powerup_distribution():
@ -228,7 +233,7 @@ class PowerupBox(ba.Actor):
"""
super().__init__()
shared = SharedObjects.get()
factory = PowerupBoxFactory.get()
self.poweruptype = poweruptype
self._powersgiven = False
@ -270,7 +275,7 @@ class PowerupBox(ba.Actor):
'reflection': 'powerup',
'reflection_scale': [1.0],
'materials': (factory.powerup_material,
ba.sharedobj('object_material'))
shared.object_material)
}) # yapf: disable
# Animate in.

View File

@ -29,6 +29,7 @@ from typing import TYPE_CHECKING
import ba
from bastd.actor import bomb as stdbomb
from bastd.actor.powerupbox import PowerupBoxFactory
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import (Any, Sequence, Optional, Dict, List, Union, Callable,
@ -108,6 +109,7 @@ class Spaz(ba.Actor):
# pylint: disable=too-many-statements
super().__init__()
shared = SharedObjects.get()
activity = self.activity
factory = get_factory()
@ -126,7 +128,7 @@ class Spaz(ba.Actor):
self._punch_power_scale = 1.2
else:
self._punch_power_scale = factory.punch_power_scale
self.fly = ba.sharedobj('globals').happy_thoughts_mode
self.fly = ba.getactivity().globalsnode.happy_thoughts_mode
if isinstance(activity, ba.GameActivity):
self._hockey = activity.map.is_hockey
else:
@ -135,14 +137,10 @@ class Spaz(ba.Actor):
self._cursed = False
self._connected_to_player: Optional[ba.Player] = None
materials = [
factory.spaz_material,
ba.sharedobj('object_material'),
ba.sharedobj('player_material')
]
roller_materials = [
factory.roller_material,
ba.sharedobj('player_material')
factory.spaz_material, shared.object_material,
shared.player_material
]
roller_materials = [factory.roller_material, shared.player_material]
extras_material = []
if can_accept_powerups:
@ -152,8 +150,8 @@ class Spaz(ba.Actor):
extras_material.append(pam)
media = factory.get_media(character)
punchmats = (factory.punch_material, ba.sharedobj('attack_material'))
pickupmats = (factory.pickup_material, ba.sharedobj('pickup_material'))
punchmats = (factory.punch_material, shared.attack_material)
pickupmats = (factory.pickup_material, shared.pickup_material)
self.node: ba.Node = ba.newnode(
type='spaz',
delegate=self,

View File

@ -24,9 +24,11 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import _ba
import ba
from bastd.actor import spaz as basespaz
from bastd.gameutils import SharedObjects
from bastd.actor.spaz import (PickupMessage, PunchHitMessage,
CurseExplodeMessage)
import _ba
if TYPE_CHECKING:
from typing import Any, Dict
@ -101,6 +103,7 @@ class SpazFactory:
def __init__(self) -> None:
"""Instantiate a factory object."""
shared = SharedObjects.get()
self.impact_sounds_medium = (ba.getsound('impactMedium'),
ba.getsound('impactMedium2'))
self.impact_sounds_hard = (ba.getsound('impactHard'),
@ -123,14 +126,14 @@ class SpazFactory:
self.pickup_material = ba.Material()
self.curse_material = ba.Material()
footing_material = ba.sharedobj('footing_material')
object_material = ba.sharedobj('object_material')
player_material = ba.sharedobj('player_material')
region_material = ba.sharedobj('region_material')
footing_material = shared.footing_material
object_material = shared.object_material
player_material = shared.player_material
region_material = shared.region_material
# send footing messages to spazzes so they know when they're on solid
# ground.
# eww this should really just be built into the spaz node
# Send footing messages to spazzes so they know when they're on
# solid ground.
# Eww; this probably should just be built into the spaz node.
self.roller_material.add_actions(
conditions=('they_have_material', footing_material),
actions=(('message', 'our_node', 'at_connect', 'footing', 1),
@ -140,27 +143,36 @@ class SpazFactory:
conditions=('they_have_material', footing_material),
actions=(('message', 'our_node', 'at_connect', 'footing', 1),
('message', 'our_node', 'at_disconnect', 'footing', -1)))
# punches
# Punches.
self.punch_material.add_actions(
conditions=('they_are_different_node_than_us', ),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect',
basespaz.PunchHitMessage())))
# pickups
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect', PunchHitMessage()),
))
# Pickups.
self.pickup_material.add_actions(
conditions=(('they_are_different_node_than_us', ), 'and',
('they_have_material', object_material)),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect',
basespaz.PickupMessage())))
# curse
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect', PickupMessage()),
))
# Curse.
self.curse_material.add_actions(
conditions=(('they_are_different_node_than_us', ), 'and',
('they_have_material', player_material)),
conditions=(
('they_are_different_node_than_us', ),
'and',
('they_have_material', player_material),
),
actions=('message', 'our_node', 'at_connect',
basespaz.CurseExplodeMessage()))
CurseExplodeMessage()),
)
self.foot_impact_sounds = (ba.getsound('footImpact01'),
ba.getsound('footImpact02'),
@ -171,34 +183,45 @@ class SpazFactory:
self.roller_material.add_actions(
conditions=('they_have_material', footing_material),
actions=(('impact_sound', self.foot_impact_sounds, 1,
0.2), ('skid_sound', self.foot_skid_sound, 20, 0.3),
('roll_sound', self.foot_roll_sound, 20, 3.0)))
actions=(
('impact_sound', self.foot_impact_sounds, 1, 0.2),
('skid_sound', self.foot_skid_sound, 20, 0.3),
('roll_sound', self.foot_roll_sound, 20, 3.0),
))
self.skid_sound = ba.getsound('gravelSkid')
self.spaz_material.add_actions(
conditions=('they_have_material', footing_material),
actions=(('impact_sound', self.foot_impact_sounds, 20,
6), ('skid_sound', self.skid_sound, 2.0, 1),
('roll_sound', self.skid_sound, 2.0, 1)))
actions=(
('impact_sound', self.foot_impact_sounds, 20, 6),
('skid_sound', self.skid_sound, 2.0, 1),
('roll_sound', self.skid_sound, 2.0, 1),
))
self.shield_up_sound = ba.getsound('shieldUp')
self.shield_down_sound = ba.getsound('shieldDown')
self.shield_hit_sound = ba.getsound('shieldHit')
# we don't want to collide with stuff we're initially overlapping
# (unless its marked with a special region material)
# We don't want to collide with stuff we're initially overlapping
# (unless its marked with a special region material).
self.spaz_material.add_actions(
conditions=((('we_are_younger_than', 51), 'and',
('they_are_different_node_than_us', )), 'and',
('they_dont_have_material', region_material)),
actions=('modify_node_collision', 'collide', False))
conditions=(
(
('we_are_younger_than', 51),
'and',
('they_are_different_node_than_us', ),
),
'and',
('they_dont_have_material', region_material),
),
actions=('modify_node_collision', 'collide', False),
)
self.spaz_media: Dict[str, Any] = {}
# lets load some basic rules (allows them to be tweaked from the
# master server)
# Lets load some basic rules.
# (allows them to be tweaked from the master server)
self.shield_decay_rate = _ba.get_account_misc_read_val('rsdr', 10.0)
self.punch_cooldown = _ba.get_account_misc_read_val('rpc', 400)
self.punch_cooldown_gloves = (_ba.get_account_misc_read_val(

View File

@ -32,6 +32,7 @@ import ba
from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.flag import Flag
from bastd.actor.scoreboard import Scoreboard
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Type, List, Dict, Sequence, Union
@ -111,6 +112,7 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
return 'touch ${ARG1} flags', self._score_to_win
def create_team(self, sessionteam: ba.SessionTeam) -> Team:
shared = SharedObjects.get()
base_pos = self.map.get_flag_position(sessionteam.id)
ba.newnode('light',
attrs={
@ -129,7 +131,7 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
mat = self._base_region_materials[sessionteam.id] = ba.Material()
mat.add_actions(
conditions=('they_have_material', ba.sharedobj('player_material')),
conditions=('they_have_material', shared.player_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),

View File

@ -31,6 +31,7 @@ import ba
from bastd.actor.flag import Flag
from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.scoreboard import Scoreboard
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Type, List, Dict, Optional, Sequence, Union
@ -142,6 +143,7 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
def on_begin(self) -> None:
super().on_begin()
shared = SharedObjects.get()
self.setup_standard_time_limit(self._time_limit)
self.setup_standard_powerup_drops()
self._flag_spawn_pos = self.map.get_flag_position(None)
@ -155,7 +157,7 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
mat.add_actions(
conditions=(
'they_have_material',
ba.sharedobj('player_material'),
shared.player_material,
),
actions=(
('modify_part_collision', 'collide', True),
@ -329,8 +331,7 @@ class ChosenOneGame(ba.TeamGameActivity[Player, Team]):
super().handlemessage(msg)
player = msg.getplayer(Player)
if player is self._get_chosen_one_player():
killerplayer = ba.playercast_o(Player,
msg.getkillerplayer(Player))
killerplayer = msg.getkillerplayer(Player)
self._set_chosen_one_player(None if (
killerplayer is None or killerplayer is player
or not killerplayer.is_alive()) else killerplayer)

View File

@ -32,6 +32,7 @@ import ba
from bastd.actor.flag import Flag
from bastd.actor.scoreboard import Scoreboard
from bastd.actor.playerspaz import PlayerSpaz
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Optional, Type, List, Dict, Sequence, Union
@ -119,6 +120,7 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]):
super().__init__(settings)
shared = SharedObjects.get()
self._scoreboard = Scoreboard()
self._score_sound = ba.getsound('score')
self._swipsound = ba.getsound('swip')
@ -134,7 +136,7 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
# We want flags to tell us they've been hit but not react physically.
self._extraflagmat.add_actions(
conditions=('they_have_material', ba.sharedobj('player_material')),
conditions=('they_have_material', shared.player_material),
actions=(('modify_part_collision', 'collide', True),
('call', 'at_connect', self._handle_flag_player_collide)))

View File

@ -35,6 +35,7 @@ from bastd.actor.spazbot import SpazBotSet, BouncyBot, SpazBotDiedMessage
from bastd.actor.onscreencountdown import OnScreenCountdown
from bastd.actor.scoreboard import Scoreboard
from bastd.actor.respawnicon import RespawnIcon
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Type, Dict, List, Tuple, Optional
@ -78,6 +79,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]):
super().__init__(settings)
shared = SharedObjects.get()
self._last_player_death_time = None
self._scoreboard = Scoreboard()
self.egg_model = ba.getmodel('egg')
@ -89,7 +91,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
self._max_eggs = 1.0
self.egg_material = ba.Material()
self.egg_material.add_actions(
conditions=('they_have_material', ba.sharedobj('player_material')),
conditions=('they_have_material', shared.player_material),
actions=(('call', 'at_connect', self._on_egg_player_collide), ))
self._eggs: List[Egg] = []
self._update_timer: Optional[ba.Timer] = None
@ -243,12 +245,13 @@ class Egg(ba.Actor):
super().__init__()
activity = self.activity
assert isinstance(activity, EasterEggHuntGame)
shared = SharedObjects.get()
# Spawn just above the provided point.
self._spawn_pos = (position[0], position[1] + 1.0, position[2])
ctex = (activity.egg_tex_1, activity.egg_tex_2,
activity.egg_tex_3)[random.randrange(3)]
mats = [ba.sharedobj('object_material'), activity.egg_material]
mats = [shared.object_material, activity.egg_material]
self.node = ba.newnode('prop',
delegate=self,
attrs={

View File

@ -530,8 +530,8 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]):
}))
self._time_text_input = ba.NodeActor(
ba.newnode('timedisplay', attrs={'showsubseconds': True}))
ba.sharedobj('globals').connectattr('time', self._time_text_input.node,
'time2')
self.globalsnode.connectattr('time', self._time_text_input.node,
'time2')
assert self._time_text_input.node
assert self._time_text.node
self._time_text_input.node.connectattr('output', self._time_text.node,

View File

@ -31,6 +31,7 @@ import ba
from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.scoreboard import Scoreboard
from bastd.actor.powerupbox import PowerupBoxFactory
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence, Dict, Type, List, Optional, Union
@ -48,6 +49,7 @@ class Puck(ba.Actor):
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0)):
super().__init__()
shared = SharedObjects.get()
activity = self.getactivity()
# Spawn just above the provided point.
@ -56,7 +58,7 @@ class Puck(ba.Actor):
self.scored = False
assert activity is not None
assert isinstance(activity, HockeyGame)
pmats = [ba.sharedobj('object_material'), activity.puck_material]
pmats = [shared.object_material, activity.puck_material]
self.node = ba.newnode('prop',
delegate=self,
attrs={
@ -153,6 +155,7 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]):
super().__init__(settings)
shared = SharedObjects.get()
self._scoreboard = Scoreboard()
self._cheer_sound = ba.getsound('cheer')
self._chant_sound = ba.getsound('crowdChant')
@ -165,22 +168,26 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
self.puck_material = ba.Material()
self.puck_material.add_actions(actions=(('modify_part_collision',
'friction', 0.5)))
self.puck_material.add_actions(conditions=('they_have_material',
shared.pickup_material),
actions=('modify_part_collision',
'collide', False))
self.puck_material.add_actions(
conditions=('they_have_material', ba.sharedobj('pickup_material')),
actions=('modify_part_collision', 'collide', False))
self.puck_material.add_actions(
conditions=(('we_are_younger_than', 100),
'and', ('they_have_material',
ba.sharedobj('object_material'))),
actions=('modify_node_collision', 'collide', False))
self.puck_material.add_actions(
conditions=('they_have_material',
ba.sharedobj('footing_material')),
actions=('impact_sound', self._puck_sound, 0.2, 5))
conditions=(
('we_are_younger_than', 100),
'and',
('they_have_material', shared.object_material),
),
actions=('modify_node_collision', 'collide', False),
)
self.puck_material.add_actions(conditions=('they_have_material',
shared.footing_material),
actions=('impact_sound',
self._puck_sound, 0.2, 5))
# Keep track of which player last touched the puck
self.puck_material.add_actions(
conditions=('they_have_material', ba.sharedobj('player_material')),
conditions=('they_have_material', shared.player_material),
actions=(('call', 'at_connect',
self._handle_puck_player_collide), ))

View File

@ -33,6 +33,7 @@ import ba
from bastd.actor.flag import Flag
from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.scoreboard import Scoreboard
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from weakref import ReferenceType
@ -97,6 +98,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]):
super().__init__(settings)
shared = SharedObjects.get()
self._scoreboard = Scoreboard()
self._swipsound = ba.getsound('swip')
self._tick_sound = ba.getsound('tick')
@ -121,7 +123,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
self._time_limit = float(settings['Time Limit'])
self._flag_region_material = ba.Material()
self._flag_region_material.add_actions(
conditions=('they_have_material', ba.sharedobj('player_material')),
conditions=('they_have_material', shared.player_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
@ -145,6 +147,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
def on_begin(self) -> None:
super().on_begin()
shared = SharedObjects.get()
self.setup_standard_time_limit(self._time_limit)
self.setup_standard_powerup_drops()
self._flag_pos = self.map.get_flag_position(None)
@ -164,10 +167,7 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
'color': (0.2, 0.2, 0.2)
})
# Flag region.
flagmats = [
self._flag_region_material,
ba.sharedobj('region_material')
]
flagmats = [self._flag_region_material, shared.region_material]
ba.newnode('region',
attrs={
'position': self._flag_pos,

View File

@ -33,6 +33,7 @@ import ba
from bastd.actor.bomb import Bomb
from bastd.actor.playerspaz import PlayerSpaz
from bastd.actor.scoreboard import Scoreboard
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import (Any, Type, Tuple, List, Sequence, Optional, Dict,
@ -196,14 +197,17 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
def on_transition_in(self) -> None:
super().on_transition_in()
shared = SharedObjects.get()
pts = self.map.get_def_points('race_point')
mat = self.race_region_material = ba.Material()
mat.add_actions(conditions=('they_have_material',
ba.sharedobj('player_material')),
actions=(('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical',
False), ('call', 'at_connect',
self._handle_race_point_collide)))
shared.player_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('call', 'at_connect',
self._handle_race_point_collide),
))
for rpt in pts:
self._regions.append(RaceRegion(rpt, len(self._regions)))

View File

@ -34,6 +34,7 @@ from bastd.actor.bomb import TNTSpawner
from bastd.actor.scoreboard import Scoreboard
from bastd.actor.respawnicon import RespawnIcon
from bastd.actor.powerupbox import PowerupBox, PowerupBoxFactory
from bastd.gameutils import SharedObjects
from bastd.actor.spazbot import (
SpazBotSet, SpazBot, SpazBotDiedMessage, BomberBot, BrawlerBot, TriggerBot,
TriggerBotPro, BomberBotProShielded, TriggerBotProShielded, ChargerBot,
@ -88,6 +89,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
def __init__(self, settings: Dict[str, Any]):
settings['map'] = 'Tower D'
super().__init__(settings)
shared = SharedObjects.get()
self._preset = self.settings_raw.get('preset', 'pro')
self._player_death_sound = ba.getsound('playerDeath')
@ -109,7 +111,7 @@ class RunaroundGame(ba.CoopGameActivity[Player, Team]):
self._score_region_material = ba.Material()
self._score_region_material.add_actions(
conditions=('they_have_material', ba.sharedobj('player_material')),
conditions=('they_have_material', shared.player_material),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('call', 'at_connect', self._handle_reached_end)))

View File

@ -171,9 +171,9 @@ class TargetPracticeGame(ba.TeamGameActivity[Player, Team]):
# Feed the explosion point to all our targets and get points in return.
# Note: we operate on a copy of self._targets since the list may change
# under us if we hit stuff (don't wanna get points for new targets).
player = ba.playercast_o(Player, bomb.get_source_player())
player = bomb.get_source_player(Player)
if not player:
return # could happen if they leave after throwing a bomb..
return # Could happen if they leave after throwing a bomb.
bullseye = any(
target.do_hit_at_position(pos, player)

View File

@ -24,5 +24,142 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import ba
if TYPE_CHECKING:
from typing import Sequence
from typing import Sequence, Optional
# Attr we store these objects as on the current activity.
# (based on our module so hopefully avoids conflicts)
STORAGE_ATTR_NAME = '_' + __name__.replace('.', '_') + '_sharedobjs'
class SharedObjects:
"""Various common components for use in games.
Category: Gameplay Classes
Objects contained here are created on-demand as accessed and shared
by everything in the current activity. This includes things such as
standard materials.
"""
def __init__(self) -> None:
activity = ba.getactivity()
if hasattr(activity, STORAGE_ATTR_NAME):
raise RuntimeError('Use SharedObjects.get() to fetch the'
' shared instance for this activity.')
self._object_material: Optional[ba.Material] = None
self._player_material: Optional[ba.Material] = None
self._pickup_material: Optional[ba.Material] = None
self._footing_material: Optional[ba.Material] = None
self._attack_material: Optional[ba.Material] = None
self._death_material: Optional[ba.Material] = None
self._region_material: Optional[ba.Material] = None
self._railing_material: Optional[ba.Material] = None
@staticmethod
def get() -> SharedObjects:
"""Fetch/create the instance of this class for the current activity."""
activity = ba.getactivity()
shobs = getattr(activity, STORAGE_ATTR_NAME, None)
if shobs is None:
shobs = SharedObjects()
setattr(activity, STORAGE_ATTR_NAME, shobs)
assert isinstance(shobs, SharedObjects)
return shobs
@property
def player_material(self) -> ba.Material:
"""a ba.Material to be applied to player parts. Generally,
materials related to the process of scoring when reaching a goal, etc
will look for the presence of this material on things that hit them.
"""
if self._player_material is None:
self._player_material = ba.Material()
return self._player_material
@property
def object_material(self) -> ba.Material:
"""A ba.Material that should be applied to any small,
normal, physical objects such as bombs, boxes, players, etc. Other
materials often check for the presence of this material as a
prerequisite for performing certain actions (such as disabling
collisions between initially-overlapping objects)
"""
if self._object_material is None:
self._object_material = ba.Material()
return self._object_material
@property
def pickup_material(self) -> ba.Material:
"""A ba.Material; collision shapes used for picking things
up will have this material applied. To prevent an object from being
picked up, you can add a material that disables collisions against
things containing this material.
"""
if self._pickup_material is None:
self._pickup_material = ba.Material()
return self._pickup_material
@property
def footing_material(self) -> ba.Material:
"""Anything that can be 'walked on' should have this
ba.Material applied; generally just terrain and whatnot. A character
will snap upright whenever touching something with this material so it
should not be applied to props, etc.
"""
if self._footing_material is None:
self._footing_material = ba.Material()
return self._footing_material
@property
def attack_material(self) -> ba.Material:
"""A ba.Material applied to explosion shapes, punch
shapes, etc. An object not wanting to receive impulse/etc messages can
disable collisions against this material.
"""
if self._attack_material is None:
self._attack_material = ba.Material()
return self._attack_material
@property
def death_material(self) -> ba.Material:
"""A ba.Material that sends a ba.DieMessage() to anything
that touches it; handy for terrain below a cliff, etc.
"""
if self._death_material is None:
mat = self._death_material = ba.Material()
mat.add_actions(
('message', 'their_node', 'at_connect', ba.DieMessage()))
return self._death_material
@property
def region_material(self) -> ba.Material:
"""A ba.Material used for non-physical collision shapes
(regions); collisions can generally be allowed with this material even
when initially overlapping since it is not physical.
"""
if self._region_material is None:
self._region_material = ba.Material()
return self._region_material
@property
def railing_material(self) -> ba.Material:
"""A ba.Material with a very low friction/stiffness/etc
that can be applied to invisible 'railings' useful for gently keeping
characters from falling off of cliffs.
"""
if self._railing_material is None:
mat = self._railing_material = ba.Material()
mat.add_actions(('modify_part_collision', 'collide', False))
mat.add_actions(('modify_part_collision', 'stiffness', 0.003))
mat.add_actions(('modify_part_collision', 'damping', 0.00001))
mat.add_actions(
conditions=('they_have_material', self.player_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'friction', 0.0),
),
)
return self._railing_material

View File

@ -182,7 +182,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
vr_bottom_fill_model = ba.getmodel('thePadVRFillBottom')
vr_top_fill_model = ba.getmodel('thePadVRFillTop')
gnode = ba.sharedobj('globals')
gnode = self.globalsnode
gnode.camera_mode = 'rotate'
tint = (1.14, 1.1, 1.0)

View File

@ -26,7 +26,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import ba
# from bastd import stdmap
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, List, Dict
@ -65,6 +65,7 @@ class HockeyStadium(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode('terrain',
delegate=self,
attrs={
@ -75,7 +76,7 @@ class HockeyStadium(ba.Map):
'color_texture':
self.preloaddata['tex'],
'materials': [
ba.sharedobj('footing_material'),
shared.footing_material,
self.preloaddata['ice_material']
]
})
@ -87,9 +88,7 @@ class HockeyStadium(ba.Map):
'background': True,
'color_texture': self.preloaddata['stands_tex']
})
mats = [
ba.sharedobj('footing_material'), self.preloaddata['ice_material']
]
mats = [shared.footing_material, self.preloaddata['ice_material']]
self.floor = ba.newnode('terrain',
attrs={
'model': self.preloaddata['models'][1],
@ -105,7 +104,7 @@ class HockeyStadium(ba.Map):
'visible_in_reflections': False,
'color_texture': self.preloaddata['stands_tex']
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.floor_reflection = True
gnode.debris_friction = 0.3
gnode.debris_kill_height = -0.3
@ -145,6 +144,7 @@ class FootballStadium(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -152,7 +152,7 @@ class FootballStadium(ba.Map):
'model': self.preloaddata['model'],
'collide_model': self.preloaddata['collide_model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
ba.newnode('terrain',
attrs={
@ -162,7 +162,7 @@ class FootballStadium(ba.Map):
'background': True,
'color_texture': self.preloaddata['tex']
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.3, 1.2, 1.0)
gnode.ambient_color = (1.3, 1.2, 1.0)
gnode.vignette_outer = (0.57, 0.57, 0.57)
@ -218,6 +218,7 @@ class Bridgit(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -225,7 +226,7 @@ class Bridgit(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model_top'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.bottom = ba.newnode('terrain',
attrs={
@ -253,7 +254,7 @@ class Bridgit(ba.Map):
'terrain',
attrs={
'collide_model': self.preloaddata['railing_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
self.bg_collide = ba.newnode('terrain',
@ -261,12 +262,12 @@ class Bridgit(ba.Map):
'collide_model':
self.preloaddata['collide_bg'],
'materials': [
ba.sharedobj('footing_material'),
shared.footing_material,
self.preloaddata['bg_material'],
ba.sharedobj('death_material')
shared.death_material
]
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.1, 1.2, 1.3)
gnode.ambient_color = (1.1, 1.2, 1.3)
gnode.vignette_outer = (0.65, 0.6, 0.55)
@ -312,6 +313,7 @@ class BigG(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -320,7 +322,7 @@ class BigG(ba.Map):
'color': (0.7, 0.7, 0.7),
'model': self.preloaddata['model_top'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.bottom = ba.newnode('terrain',
attrs={
@ -349,7 +351,7 @@ class BigG(ba.Map):
'terrain',
attrs={
'collide_model': self.preloaddata['bumper_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
self.bg_collide = ba.newnode('terrain',
@ -357,12 +359,12 @@ class BigG(ba.Map):
'collide_model':
self.preloaddata['collide_bg'],
'materials': [
ba.sharedobj('footing_material'),
shared.footing_material,
self.preloaddata['bg_material'],
ba.sharedobj('death_material')
shared.death_material
]
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.1, 1.2, 1.3)
gnode.ambient_color = (1.1, 1.2, 1.3)
gnode.vignette_outer = (0.65, 0.6, 0.55)
@ -406,6 +408,7 @@ class Roundabout(ba.Map):
def __init__(self) -> None:
super().__init__(vr_overlay_offset=(0, -1, 1))
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -413,7 +416,7 @@ class Roundabout(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.bottom = ba.newnode('terrain',
attrs={
@ -442,19 +445,19 @@ class Roundabout(ba.Map):
'collide_model':
self.preloaddata['collide_bg'],
'materials': [
ba.sharedobj('footing_material'),
shared.footing_material,
self.preloaddata['bg_material'],
ba.sharedobj('death_material')
shared.death_material
]
})
self.railing = ba.newnode(
'terrain',
attrs={
'collide_model': self.preloaddata['railing_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.0, 1.05, 1.1)
gnode.ambient_color = (1.0, 1.05, 1.1)
gnode.shadow_ortho = True
@ -499,6 +502,7 @@ class MonkeyFace(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -506,7 +510,7 @@ class MonkeyFace(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.bottom = ba.newnode('terrain',
attrs={
@ -535,19 +539,19 @@ class MonkeyFace(ba.Map):
'collide_model':
self.preloaddata['collide_bg'],
'materials': [
ba.sharedobj('footing_material'),
shared.footing_material,
self.preloaddata['bg_material'],
ba.sharedobj('death_material')
shared.death_material
]
})
self.railing = ba.newnode(
'terrain',
attrs={
'collide_model': self.preloaddata['railing_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.1, 1.2, 1.2)
gnode.ambient_color = (1.2, 1.3, 1.3)
gnode.vignette_outer = (0.60, 0.62, 0.66)
@ -593,6 +597,7 @@ class ZigZag(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -600,7 +605,7 @@ class ZigZag(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.background = ba.newnode(
'terrain',
@ -628,19 +633,19 @@ class ZigZag(ba.Map):
'collide_model':
self.preloaddata['collide_bg'],
'materials': [
ba.sharedobj('footing_material'),
shared.footing_material,
self.preloaddata['bg_material'],
ba.sharedobj('death_material')
shared.death_material
]
})
self.railing = ba.newnode(
'terrain',
attrs={
'collide_model': self.preloaddata['railing_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.0, 1.15, 1.15)
gnode.ambient_color = (1.0, 1.15, 1.15)
gnode.vignette_outer = (0.57, 0.59, 0.63)
@ -682,6 +687,7 @@ class ThePad(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -689,7 +695,7 @@ class ThePad(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.bottom = ba.newnode('terrain',
attrs={
@ -709,7 +715,7 @@ class ThePad(ba.Map):
'terrain',
attrs={
'collide_model': self.preloaddata['railing_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
ba.newnode('terrain',
@ -721,7 +727,7 @@ class ThePad(ba.Map):
'background': True,
'color_texture': self.preloaddata['vr_fill_mound_tex']
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.1, 1.1, 1.0)
gnode.ambient_color = (1.1, 1.1, 1.0)
gnode.vignette_outer = (0.7, 0.65, 0.75)
@ -760,6 +766,7 @@ class DoomShroom(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -767,7 +774,7 @@ class DoomShroom(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.background = ba.newnode(
'terrain',
@ -791,16 +798,13 @@ class DoomShroom(ba.Map):
'lighting': False,
'color_texture': self.preloaddata['tex']
})
self.bg_collide = ba.newnode('terrain',
attrs={
'collide_model':
self.preloaddata['collide_bg'],
'materials': [
ba.sharedobj('footing_material'),
ba.sharedobj('death_material')
]
})
gnode = ba.sharedobj('globals')
self.bg_collide = ba.newnode(
'terrain',
attrs={
'collide_model': self.preloaddata['collide_bg'],
'materials': [shared.footing_material, shared.death_material]
})
gnode = ba.getactivity().globalsnode
gnode.tint = (0.82, 1.10, 1.15)
gnode.ambient_color = (0.9, 1.3, 1.1)
gnode.shadow_ortho = False
@ -854,6 +858,7 @@ class LakeFrigid(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode('terrain',
delegate=self,
attrs={
@ -864,7 +869,7 @@ class LakeFrigid(ba.Map):
'color_texture':
self.preloaddata['tex'],
'materials': [
ba.sharedobj('footing_material'),
shared.footing_material,
self.preloaddata['ice_material']
]
})
@ -890,7 +895,7 @@ class LakeFrigid(ba.Map):
'background': True,
'color_texture': self.preloaddata['tex']
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1, 1, 1)
gnode.ambient_color = (1, 1, 1)
gnode.shadow_ortho = True
@ -931,6 +936,7 @@ class TipTop(ba.Map):
def __init__(self) -> None:
super().__init__(vr_overlay_offset=(0, -0.2, 2.5))
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -939,7 +945,7 @@ class TipTop(ba.Map):
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'color': (0.7, 0.7, 0.7),
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.bottom = ba.newnode('terrain',
attrs={
@ -961,10 +967,10 @@ class TipTop(ba.Map):
'terrain',
attrs={
'collide_model': self.preloaddata['railing_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (0.8, 0.9, 1.3)
gnode.ambient_color = (0.8, 0.9, 1.3)
gnode.vignette_outer = (0.79, 0.79, 0.69)
@ -1006,6 +1012,7 @@ class CragCastle(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -1013,7 +1020,7 @@ class CragCastle(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.bottom = ba.newnode('terrain',
attrs={
@ -1033,7 +1040,7 @@ class CragCastle(ba.Map):
'terrain',
attrs={
'collide_model': self.preloaddata['railing_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
ba.newnode('terrain',
@ -1045,7 +1052,7 @@ class CragCastle(ba.Map):
'background': True,
'color_texture': self.preloaddata['vr_fill_mound_tex']
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.shadow_ortho = True
gnode.shadow_offset = (0, 0, -5.0)
gnode.tint = (1.15, 1.05, 0.75)
@ -1106,6 +1113,7 @@ class TowerD(ba.Map):
def __init__(self) -> None:
super().__init__(vr_overlay_offset=(0, 1, 1))
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -1113,7 +1121,7 @@ class TowerD(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.node_bottom = ba.newnode(
'terrain',
@ -1147,7 +1155,7 @@ class TowerD(ba.Map):
'affect_bg_dynamics': False,
'materials': [self.preloaddata['player_wall_material']]
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.15, 1.11, 1.03)
gnode.ambient_color = (1.2, 1.1, 1.0)
gnode.vignette_outer = (0.7, 0.73, 0.7)
@ -1209,6 +1217,7 @@ class HappyThoughts(ba.Map):
def __init__(self) -> None:
super().__init__(vr_overlay_offset=(0, -3.7, 2.5))
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -1216,7 +1225,7 @@ class HappyThoughts(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.bottom = ba.newnode('terrain',
attrs={
@ -1241,7 +1250,7 @@ class HappyThoughts(ba.Map):
'background': True,
'color_texture': self.preloaddata['vr_fill_mound_tex']
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.happy_thoughts_mode = True
gnode.shadow_offset = (0.0, 8.0, 5.0)
gnode.tint = (1.3, 1.23, 1.0)
@ -1309,6 +1318,7 @@ class StepRightUp(ba.Map):
def __init__(self) -> None:
super().__init__(vr_overlay_offset=(0, -1, 2))
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -1316,7 +1326,7 @@ class StepRightUp(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.node_bottom = ba.newnode(
'terrain',
@ -1343,7 +1353,7 @@ class StepRightUp(ba.Map):
'background': True,
'color_texture': self.preloaddata['bgtex']
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.2, 1.1, 1.0)
gnode.ambient_color = (1.2, 1.1, 1.0)
gnode.vignette_outer = (0.7, 0.65, 0.75)
@ -1394,6 +1404,7 @@ class Courtyard(ba.Map):
def __init__(self) -> None:
super().__init__()
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -1401,7 +1412,7 @@ class Courtyard(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.background = ba.newnode(
'terrain',
@ -1437,7 +1448,7 @@ class Courtyard(ba.Map):
'affect_bg_dynamics': False,
'materials': [self.preloaddata['player_wall_material']]
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.2, 1.17, 1.1)
gnode.ambient_color = (1.2, 1.17, 1.1)
gnode.vignette_outer = (0.6, 0.6, 0.64)
@ -1489,6 +1500,7 @@ class Rampage(ba.Map):
def __init__(self) -> None:
super().__init__(vr_overlay_offset=(0, 0, 2))
shared = SharedObjects.get()
self.node = ba.newnode(
'terrain',
delegate=self,
@ -1496,7 +1508,7 @@ class Rampage(ba.Map):
'collide_model': self.preloaddata['collide_model'],
'model': self.preloaddata['model'],
'color_texture': self.preloaddata['tex'],
'materials': [ba.sharedobj('footing_material')]
'materials': [shared.footing_material]
})
self.background = ba.newnode(
'terrain',
@ -1531,10 +1543,10 @@ class Rampage(ba.Map):
'terrain',
attrs={
'collide_model': self.preloaddata['railing_collide_model'],
'materials': [ba.sharedobj('railing_material')],
'materials': [shared.railing_material],
'bumper': True
})
gnode = ba.sharedobj('globals')
gnode = ba.getactivity().globalsnode
gnode.tint = (1.2, 1.1, 0.97)
gnode.ambient_color = (1.3, 1.2, 1.03)
gnode.vignette_outer = (0.62, 0.64, 0.69)

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2020-05-28 for Ballistica version 1.5.0 build 20033</em></h4>
<h4><em>last updated on 2020-05-29 for Ballistica version 1.5.0 build 20033</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>
@ -40,6 +40,7 @@
</ul>
<li><a href="#class_ba_SessionPlayer">ba.SessionPlayer</a></li>
<li><a href="#class_ba_SessionTeam">ba.SessionTeam</a></li>
<li><a href="#class_ba_StandLocation">ba.StandLocation</a></li>
<li><a href="#class_ba_Stats">ba.Stats</a></li>
<li><a href="#class_ba_Team">ba.Team</a></li>
<li><a href="#class_ba_TeamGameResults">ba.TeamGameResults</a></li>
@ -61,7 +62,6 @@
<li><a href="#function_ba_playsound">ba.playsound()</a></li>
<li><a href="#function_ba_printnodes">ba.printnodes()</a></li>
<li><a href="#function_ba_setmusic">ba.setmusic()</a></li>
<li><a href="#function_ba_sharedobj">ba.sharedobj()</a></li>
<li><a href="#function_ba_show_damage_count">ba.show_damage_count()</a></li>
</ul>
<h4><a name="class_category_General_Utility_Classes">General Utility Classes</a></h4>
@ -345,7 +345,7 @@ actually award achievements.</p>
can overlap during transitions.</p>
<h3>Attributes:</h3>
<h5><a href="#attr_ba_Activity__expired">expired</a>, <a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__playertype">playertype</a>, <a href="#attr_ba_Activity__session">session</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__stats">stats</a>, <a href="#attr_ba_Activity__teams">teams</a>, <a href="#attr_ba_Activity__teamtype">teamtype</a></h5>
<h5><a href="#attr_ba_Activity__expired">expired</a>, <a href="#attr_ba_Activity__globalsnode">globalsnode</a>, <a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__playertype">playertype</a>, <a href="#attr_ba_Activity__session">session</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__stats">stats</a>, <a href="#attr_ba_Activity__teams">teams</a>, <a href="#attr_ba_Activity__teamtype">teamtype</a></h5>
<dl>
<dt><h4><a name="attr_ba_Activity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p>
@ -355,6 +355,12 @@ actually award achievements.</p>
At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.</p>
</dd>
<dt><h4><a name="attr_ba_Activity__globalsnode">globalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The 'globals' <a href="#class_ba_Node">ba.Node</a> for the activity. This contains various
global controls and values.</p>
</dd>
<dt><h4><a name="attr_ba_Activity__players">players</a></h4></dt><dd>
<p><span>List[PlayerType]</span></p>
@ -1541,7 +1547,7 @@ start_long_action(callback_when_done=<a href="#class_ba_ContextCall">ba.ContextC
<h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5>
<h3>Attributes Defined Here:</h3>
<h5><a href="#attr_ba_CoopGameActivity__expired">expired</a>, <a href="#attr_ba_CoopGameActivity__map">map</a>, <a href="#attr_ba_CoopGameActivity__playertype">playertype</a>, <a href="#attr_ba_CoopGameActivity__session">session</a>, <a href="#attr_ba_CoopGameActivity__stats">stats</a>, <a href="#attr_ba_CoopGameActivity__teamtype">teamtype</a></h5>
<h5><a href="#attr_ba_CoopGameActivity__expired">expired</a>, <a href="#attr_ba_CoopGameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_CoopGameActivity__map">map</a>, <a href="#attr_ba_CoopGameActivity__playertype">playertype</a>, <a href="#attr_ba_CoopGameActivity__session">session</a>, <a href="#attr_ba_CoopGameActivity__stats">stats</a>, <a href="#attr_ba_CoopGameActivity__teamtype">teamtype</a></h5>
<dl>
<dt><h4><a name="attr_ba_CoopGameActivity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p>
@ -1551,6 +1557,12 @@ start_long_action(callback_when_done=<a href="#class_ba_ContextCall">ba.ContextC
At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.</p>
</dd>
<dt><h4><a name="attr_ba_CoopGameActivity__globalsnode">globalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The 'globals' <a href="#class_ba_Node">ba.Node</a> for the activity. This contains various
global controls and values.</p>
</dd>
<dt><h4><a name="attr_ba_CoopGameActivity__map">map</a></h4></dt><dd>
<p><span><a href="#class_ba_Map">ba.Map</a></span></p>
@ -1660,12 +1672,18 @@ and it should begin its actual game logic.</p>
<h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Session__allow_mid_activity_joins">allow_mid_activity_joins</a>, <a href="#attr_ba_Session__lobby">lobby</a>, <a href="#attr_ba_Session__max_players">max_players</a>, <a href="#attr_ba_Session__min_players">min_players</a>, <a href="#attr_ba_Session__players">players</a>, <a href="#attr_ba_Session__teams">teams</a>, <a href="#attr_ba_Session__use_team_colors">use_team_colors</a>, <a href="#attr_ba_Session__use_teams">use_teams</a></h5>
<h3>Attributes Defined Here:</h3>
<h5><a href="#attr_ba_CoopSession__campaign">campaign</a>, <a href="#attr_ba_CoopSession__sessionglobalsnode">sessionglobalsnode</a></h5>
<dl>
<dt><h4><a name="attr_ba_CoopSession__campaign">campaign</a></h4></dt><dd>
<p><span>Optional[<a href="#class_ba_Campaign">ba.Campaign</a>]</span></p>
<p>The <a href="#class_ba_Campaign">ba.Campaign</a> instance this Session represents, or None if
there is no associated Campaign.</p>
</dd>
<dt><h4><a name="attr_ba_CoopSession__sessionglobalsnode">sessionglobalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The sessionglobals <a href="#class_ba_Node">ba.Node</a> for the session.</p>
</dd>
</dl>
<h3>Methods Inherited:</h3>
@ -2011,6 +2029,14 @@ its time with lingering corpses, sound effects, etc.</p>
<h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Session__allow_mid_activity_joins">allow_mid_activity_joins</a>, <a href="#attr_ba_Session__lobby">lobby</a>, <a href="#attr_ba_Session__max_players">max_players</a>, <a href="#attr_ba_Session__min_players">min_players</a>, <a href="#attr_ba_Session__players">players</a>, <a href="#attr_ba_Session__teams">teams</a>, <a href="#attr_ba_Session__use_team_colors">use_team_colors</a>, <a href="#attr_ba_Session__use_teams">use_teams</a></h5>
<h3>Attributes Defined Here:</h3>
<dl>
<dt><h4><a name="attr_ba_DualTeamSession__sessionglobalsnode">sessionglobalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The sessionglobals <a href="#class_ba_Node">ba.Node</a> for the session.</p>
</dd>
</dl>
<h3>Methods Inherited:</h3>
<h5><a href="#method_ba_MultiTeamSession__announce_game_results">announce_game_results()</a>, <a href="#method_ba_MultiTeamSession__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_MultiTeamSession__end">end()</a>, <a href="#method_ba_MultiTeamSession__end_activity">end_activity()</a>, <a href="#method_ba_MultiTeamSession__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_MultiTeamSession__get_ffa_series_length">get_ffa_series_length()</a>, <a href="#method_ba_MultiTeamSession__get_game_number">get_game_number()</a>, <a href="#method_ba_MultiTeamSession__get_max_players">get_max_players()</a>, <a href="#method_ba_MultiTeamSession__get_next_game_description">get_next_game_description()</a>, <a href="#method_ba_MultiTeamSession__get_series_length">get_series_length()</a>, <a href="#method_ba_MultiTeamSession__getactivity">getactivity()</a>, <a href="#method_ba_MultiTeamSession__handlemessage">handlemessage()</a>, <a href="#method_ba_MultiTeamSession__launch_end_session_activity">launch_end_session_activity()</a>, <a href="#method_ba_MultiTeamSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_MultiTeamSession__on_player_leave">on_player_leave()</a>, <a href="#method_ba_MultiTeamSession__on_player_request">on_player_request()</a>, <a href="#method_ba_MultiTeamSession__on_team_join">on_team_join()</a>, <a href="#method_ba_MultiTeamSession__on_team_leave">on_team_leave()</a>, <a href="#method_ba_MultiTeamSession__set_activity">set_activity()</a>, <a href="#method_ba_MultiTeamSession__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5>
<h3>Methods Defined or Overridden:</h3>
@ -2049,6 +2075,14 @@ its time with lingering corpses, sound effects, etc.</p>
<h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Session__allow_mid_activity_joins">allow_mid_activity_joins</a>, <a href="#attr_ba_Session__lobby">lobby</a>, <a href="#attr_ba_Session__max_players">max_players</a>, <a href="#attr_ba_Session__min_players">min_players</a>, <a href="#attr_ba_Session__players">players</a>, <a href="#attr_ba_Session__teams">teams</a>, <a href="#attr_ba_Session__use_team_colors">use_team_colors</a>, <a href="#attr_ba_Session__use_teams">use_teams</a></h5>
<h3>Attributes Defined Here:</h3>
<dl>
<dt><h4><a name="attr_ba_FreeForAllSession__sessionglobalsnode">sessionglobalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The sessionglobals <a href="#class_ba_Node">ba.Node</a> for the session.</p>
</dd>
</dl>
<h3>Methods Inherited:</h3>
<h5><a href="#method_ba_MultiTeamSession__announce_game_results">announce_game_results()</a>, <a href="#method_ba_MultiTeamSession__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_MultiTeamSession__end">end()</a>, <a href="#method_ba_MultiTeamSession__end_activity">end_activity()</a>, <a href="#method_ba_MultiTeamSession__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_MultiTeamSession__get_ffa_series_length">get_ffa_series_length()</a>, <a href="#method_ba_MultiTeamSession__get_game_number">get_game_number()</a>, <a href="#method_ba_MultiTeamSession__get_max_players">get_max_players()</a>, <a href="#method_ba_MultiTeamSession__get_next_game_description">get_next_game_description()</a>, <a href="#method_ba_MultiTeamSession__get_series_length">get_series_length()</a>, <a href="#method_ba_MultiTeamSession__getactivity">getactivity()</a>, <a href="#method_ba_MultiTeamSession__handlemessage">handlemessage()</a>, <a href="#method_ba_MultiTeamSession__launch_end_session_activity">launch_end_session_activity()</a>, <a href="#method_ba_MultiTeamSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_MultiTeamSession__on_player_leave">on_player_leave()</a>, <a href="#method_ba_MultiTeamSession__on_player_request">on_player_request()</a>, <a href="#method_ba_MultiTeamSession__on_team_join">on_team_join()</a>, <a href="#method_ba_MultiTeamSession__on_team_leave">on_team_leave()</a>, <a href="#method_ba_MultiTeamSession__set_activity">set_activity()</a>, <a href="#method_ba_MultiTeamSession__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5>
<h3>Methods Defined or Overridden:</h3>
@ -2098,7 +2132,7 @@ its time with lingering corpses, sound effects, etc.</p>
<h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5>
<h3>Attributes Defined Here:</h3>
<h5><a href="#attr_ba_GameActivity__expired">expired</a>, <a href="#attr_ba_GameActivity__map">map</a>, <a href="#attr_ba_GameActivity__playertype">playertype</a>, <a href="#attr_ba_GameActivity__session">session</a>, <a href="#attr_ba_GameActivity__stats">stats</a>, <a href="#attr_ba_GameActivity__teamtype">teamtype</a></h5>
<h5><a href="#attr_ba_GameActivity__expired">expired</a>, <a href="#attr_ba_GameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_GameActivity__map">map</a>, <a href="#attr_ba_GameActivity__playertype">playertype</a>, <a href="#attr_ba_GameActivity__session">session</a>, <a href="#attr_ba_GameActivity__stats">stats</a>, <a href="#attr_ba_GameActivity__teamtype">teamtype</a></h5>
<dl>
<dt><h4><a name="attr_ba_GameActivity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p>
@ -2108,6 +2142,12 @@ its time with lingering corpses, sound effects, etc.</p>
At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.</p>
</dd>
<dt><h4><a name="attr_ba_GameActivity__globalsnode">globalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The 'globals' <a href="#class_ba_Node">ba.Node</a> for the activity. This contains various
global controls and values.</p>
</dd>
<dt><h4><a name="attr_ba_GameActivity__map">map</a></h4></dt><dd>
<p><span><a href="#class_ba_Map">ba.Map</a></span></p>
@ -3299,7 +3339,7 @@ m.add_actions(actions=(('modify_part_collision', 'physical', False),
<pre><span><em><small># example 3: play some sounds when we're contacting the ground:</small></em></span>
m = <a href="#class_ba_Material">ba.Material</a>()
m.add_actions(conditions=('they_have_material',
<a href="#function_ba_sharedobj">ba.sharedobj</a>('footing_material')),
shared.footing_material),
actions=(('impact_sound', <a href="#function_ba_getsound">ba.getsound</a>('metalHit'), 2, 5),
('skid_sound', <a href="#function_ba_getsound">ba.getsound</a>('metalSkid'), 2, 5)))</pre>
@ -3329,6 +3369,14 @@ Use <a href="#function_ba_getmodel">ba.getmodel</a>() to instantiate one.</p>
<h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Session__allow_mid_activity_joins">allow_mid_activity_joins</a>, <a href="#attr_ba_Session__lobby">lobby</a>, <a href="#attr_ba_Session__max_players">max_players</a>, <a href="#attr_ba_Session__min_players">min_players</a>, <a href="#attr_ba_Session__players">players</a>, <a href="#attr_ba_Session__teams">teams</a>, <a href="#attr_ba_Session__use_team_colors">use_team_colors</a>, <a href="#attr_ba_Session__use_teams">use_teams</a></h5>
<h3>Attributes Defined Here:</h3>
<dl>
<dt><h4><a name="attr_ba_MultiTeamSession__sessionglobalsnode">sessionglobalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The sessionglobals <a href="#class_ba_Node">ba.Node</a> for the session.</p>
</dd>
</dl>
<h3>Methods Inherited:</h3>
<h5><a href="#method_ba_Session__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_Session__end">end()</a>, <a href="#method_ba_Session__end_activity">end_activity()</a>, <a href="#method_ba_Session__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_Session__getactivity">getactivity()</a>, <a href="#method_ba_Session__handlemessage">handlemessage()</a>, <a href="#method_ba_Session__launch_end_session_activity">launch_end_session_activity()</a>, <a href="#method_ba_Session__on_player_leave">on_player_leave()</a>, <a href="#method_ba_Session__on_player_request">on_player_request()</a>, <a href="#method_ba_Session__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Session__set_activity">set_activity()</a>, <a href="#method_ba_Session__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5>
<h3>Methods Defined or Overridden:</h3>
@ -4272,7 +4320,7 @@ Pass 0 or a negative number for no ban time.</p>
maintaining state between them (players, teams, score tallies, etc).</p>
<h3>Attributes:</h3>
<h5><a href="#attr_ba_Session__allow_mid_activity_joins">allow_mid_activity_joins</a>, <a href="#attr_ba_Session__lobby">lobby</a>, <a href="#attr_ba_Session__max_players">max_players</a>, <a href="#attr_ba_Session__min_players">min_players</a>, <a href="#attr_ba_Session__players">players</a>, <a href="#attr_ba_Session__teams">teams</a>, <a href="#attr_ba_Session__use_team_colors">use_team_colors</a>, <a href="#attr_ba_Session__use_teams">use_teams</a></h5>
<h5><a href="#attr_ba_Session__allow_mid_activity_joins">allow_mid_activity_joins</a>, <a href="#attr_ba_Session__lobby">lobby</a>, <a href="#attr_ba_Session__max_players">max_players</a>, <a href="#attr_ba_Session__min_players">min_players</a>, <a href="#attr_ba_Session__players">players</a>, <a href="#attr_ba_Session__sessionglobalsnode">sessionglobalsnode</a>, <a href="#attr_ba_Session__teams">teams</a>, <a href="#attr_ba_Session__use_team_colors">use_team_colors</a>, <a href="#attr_ba_Session__use_teams">use_teams</a></h5>
<dl>
<dt><h4><a name="attr_ba_Session__allow_mid_activity_joins">allow_mid_activity_joins</a></h4></dt><dd>
<p><span>bool</span></p>
@ -4305,6 +4353,11 @@ to proceed past the initial joining screen.</p>
list in <a href="#class_ba_Activity">ba.Activity</a>; not this. Some players, such as those who have
not yet selected a character, will only appear on this list.</p>
</dd>
<dt><h4><a name="attr_ba_Session__sessionglobalsnode">sessionglobalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The sessionglobals <a href="#class_ba_Node">ba.Node</a> for the session.</p>
</dd>
<dt><h4><a name="attr_ba_Session__teams">teams</a></h4></dt><dd>
<p><span>List[<a href="#class_ba_SessionTeam">ba.SessionTeam</a>]</span></p>
@ -4812,6 +4865,22 @@ of the session.</p>
<li>MIKIROG</li>
</ul>
<hr>
<h2><strong><a name="class_ba_StandLocation">ba.StandLocation</a></strong></h3>
<p><em>&lt;top level class&gt;</em>
</p>
<p>Describes a point in space and an angle to face.</p>
<p>Category: <a href="#class_category_Gameplay_Classes">Gameplay Classes</a>
</p>
<h3>Methods:</h3>
<dl>
<dt><h4><a name="method_ba_StandLocation____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.StandLocation(position: _<a href="#class_ba_Vec3">ba.Vec3</a>, angle: Optional[float] = None)</span></p>
</dd>
</dl>
<hr>
<h2><strong><a name="class_ba_StandMessage">ba.StandMessage</a></strong></h3>
<p><em>&lt;top level class&gt;</em>
</p>
@ -4961,7 +5030,7 @@ of the session.</p>
<h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5>
<h3>Attributes Defined Here:</h3>
<h5><a href="#attr_ba_TeamGameActivity__expired">expired</a>, <a href="#attr_ba_TeamGameActivity__map">map</a>, <a href="#attr_ba_TeamGameActivity__playertype">playertype</a>, <a href="#attr_ba_TeamGameActivity__session">session</a>, <a href="#attr_ba_TeamGameActivity__stats">stats</a>, <a href="#attr_ba_TeamGameActivity__teamtype">teamtype</a></h5>
<h5><a href="#attr_ba_TeamGameActivity__expired">expired</a>, <a href="#attr_ba_TeamGameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_TeamGameActivity__map">map</a>, <a href="#attr_ba_TeamGameActivity__playertype">playertype</a>, <a href="#attr_ba_TeamGameActivity__session">session</a>, <a href="#attr_ba_TeamGameActivity__stats">stats</a>, <a href="#attr_ba_TeamGameActivity__teamtype">teamtype</a></h5>
<dl>
<dt><h4><a name="attr_ba_TeamGameActivity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p>
@ -4971,6 +5040,12 @@ of the session.</p>
At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.</p>
</dd>
<dt><h4><a name="attr_ba_TeamGameActivity__globalsnode">globalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>The 'globals' <a href="#class_ba_Node">ba.Node</a> for the activity. This contains various
global controls and values.</p>
</dd>
<dt><h4><a name="attr_ba_TeamGameActivity__map">map</a></h4></dt><dd>
<p><span><a href="#class_ba_Map">ba.Map</a></span></p>
@ -6303,54 +6378,6 @@ user can override particular game music with their own.</p>
<p>if 'continuous' is True and musictype is the same as what is already
playing, the playing track will not be restarted.</p>
<hr>
<h2><strong><a name="function_ba_sharedobj">ba.sharedobj()</a></strong></h3>
<p><span>sharedobj(name: str) -&gt; Any</span></p>
<p>Return a predefined object for the current Activity, creating if needed.</p>
<p>Category: <a href="#function_category_Gameplay_Functions">Gameplay Functions</a></p>
<p>Available values for 'name':</p>
<p>'globals': returns the 'globals' <a href="#class_ba_Node">ba.Node</a>, containing various global
controls & values.</p>
<p>'object_material': a <a href="#class_ba_Material">ba.Material</a> that should be applied to any small,
normal, physical objects such as bombs, boxes, players, etc. Other
materials often check for the presence of this material as a
prerequisite for performing certain actions (such as disabling collisions
between initially-overlapping objects)</p>
<p>'player_material': a <a href="#class_ba_Material">ba.Material</a> to be applied to player parts. Generally,
materials related to the process of scoring when reaching a goal, etc
will look for the presence of this material on things that hit them.</p>
<p>'pickup_material': a <a href="#class_ba_Material">ba.Material</a>; collision shapes used for picking things
up will have this material applied. To prevent an object from being
picked up, you can add a material that disables collisions against things
containing this material.</p>
<p>'footing_material': anything that can be 'walked on' should have this
<a href="#class_ba_Material">ba.Material</a> applied; generally just terrain and whatnot. A character will
snap upright whenever touching something with this material so it should
not be applied to props, etc.</p>
<p>'attack_material': a <a href="#class_ba_Material">ba.Material</a> applied to explosion shapes, punch
shapes, etc. An object not wanting to receive impulse/etc messages can
disable collisions against this material.</p>
<p>'death_material': a <a href="#class_ba_Material">ba.Material</a> that sends a <a href="#class_ba_DieMessage">ba.DieMessage</a>() to anything
that touches it; handy for terrain below a cliff, etc.</p>
<p>'region_material': a <a href="#class_ba_Material">ba.Material</a> used for non-physical collision shapes
(regions); collisions can generally be allowed with this material even
when initially overlapping since it is not physical.</p>
<p>'railing_material': a <a href="#class_ba_Material">ba.Material</a> with a very low friction/stiffness/etc
that can be applied to invisible 'railings' useful for gently keeping
characters from falling off of cliffs.</p>
<hr>
<h2><strong><a name="function_ba_show_damage_count">ba.show_damage_count()</a></strong></h3>
<p><span>show_damage_count(damage: str, position: Sequence[float], direction: Sequence[float]) -&gt; None</span></p>