diff --git a/.efrocachemap b/.efrocachemap
index e70c719a..46f06447 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -4132,16 +4132,16 @@
"assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c",
"assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb",
"assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe",
- "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/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"
}
\ No newline at end of file
diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml
index 4859d5d1..9ab6b481 100644
--- a/.idea/dictionaries/ericf.xml
+++ b/.idea/dictionaries/ericf.xml
@@ -186,6 +186,7 @@
bname
bndl
boffs
+ bombfactory
bombsquad
bombsquadcb
bombsquadgame
@@ -1637,6 +1638,7 @@
sessionclass
sessiondata
sessionglobals
+ sessionglobalsnode
sessionname
sessionplayer
sessionteam
@@ -1655,6 +1657,7 @@
sharedobjs
shiftdelay
shiftposition
+ shobs
shortname
shouldn
showpoints
diff --git a/assets/src/ba_data/python/_ba.py b/assets/src/ba_data/python/_ba.py
index 796e7e29..942dc9d2 100644
--- a/assets/src/ba_data/python/_ba.py
+++ b/assets/src/ba_data/python/_ba.py
@@ -34,7 +34,7 @@ NOTE: This file was autogenerated by gendummymodule; do not edit by hand.
"""
# (hash we can use to see if this file is out of date)
-# SOURCES_HASH=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
diff --git a/assets/src/ba_data/python/ba/__init__.py b/assets/src/ba_data/python/ba/__init__.py
index 6b79f6e0..3576a2cd 100644
--- a/assets/src/ba_data/python/ba/__init__.py
+++ b/assets/src/ba_data/python/ba/__init__.py
@@ -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
diff --git a/assets/src/ba_data/python/ba/_activity.py b/assets/src/ba_data/python/ba/_activity.py
index 6088ed89..e46bb06d 100644
--- a/assets/src/ba_data/python/ba/_activity.py
+++ b/assets/src/ba_data/python/ba/_activity.py
@@ -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}')
diff --git a/assets/src/ba_data/python/ba/_app.py b/assets/src/ba_data/python/ba/_app.py
index 99129efc..8847c13c 100644
--- a/assets/src/ba_data/python/ba/_app.py
+++ b/assets/src/ba_data/python/ba/_app.py
@@ -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
diff --git a/assets/src/ba_data/python/ba/_coopgame.py b/assets/src/ba_data/python/ba/_coopgame.py
index ee60ea96..08c72572 100644
--- a/assets/src/ba_data/python/ba/_coopgame.py
+++ b/assets/src/ba_data/python/ba/_coopgame.py
@@ -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."""
diff --git a/assets/src/ba_data/python/ba/_gameactivity.py b/assets/src/ba_data/python/ba/_gameactivity.py
index b2bc28fa..aa74790a 100644
--- a/assets/src/ba_data/python/ba/_gameactivity.py
+++ b/assets/src/ba_data/python/ba/_gameactivity.py
@@ -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(
diff --git a/assets/src/ba_data/python/ba/_gameutils.py b/assets/src/ba_data/python/ba/_gameutils.py
index 7391925d..8c48ce76 100644
--- a/assets/src/ba_data/python/ba/_gameutils.py
+++ b/assets/src/ba_data/python/ba/_gameutils.py
@@ -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(
diff --git a/assets/src/ba_data/python/ba/_map.py b/assets/src/ba_data/python/ba/_map.py
index 9af476a8..ead68a99 100644
--- a/assets/src/ba_data/python/ba/_map.py
+++ b/assets/src/ba_data/python/ba/_map.py
@@ -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')
diff --git a/assets/src/ba_data/python/ba/_music.py b/assets/src/ba_data/python/ba/_music.py
index a1962adb..48bf5005 100644
--- a/assets/src/ba_data/python/ba/_music.py
+++ b/assets/src/ba_data/python/ba/_music.py
@@ -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
diff --git a/assets/src/ba_data/python/ba/_player.py b/assets/src/ba_data/python/ba/_player.py
index d4f6a7bf..9bea0bae 100644
--- a/assets/src/ba_data/python/ba/_player.py
+++ b/assets/src/ba_data/python/ba/_player.py
@@ -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.
diff --git a/assets/src/ba_data/python/ba/_session.py b/assets/src/ba_data/python/ba/_session.py
index 30b4f8b7..913ee62e 100644
--- a/assets/src/ba_data/python/ba/_session.py
+++ b/assets/src/ba_data/python/ba/_session.py
@@ -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."""
diff --git a/assets/src/ba_data/python/bastd/actor/bomb.py b/assets/src/ba_data/python/bastd/actor/bomb.py
index ec6cf25e..7eb080b8 100644
--- a/assets/src/ba_data/python/bastd/actor/bomb.py
+++ b/assets/src/ba_data/python/bastd/actor/bomb.py
@@ -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):
diff --git a/assets/src/ba_data/python/bastd/actor/flag.py b/assets/src/ba_data/python/bastd/actor/flag.py
index 59515af0..0e5b014f 100644
--- a/assets/src/ba_data/python/bastd/actor/flag.py
+++ b/assets/src/ba_data/python/bastd/actor/flag.py
@@ -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':
diff --git a/assets/src/ba_data/python/bastd/actor/onscreencountdown.py b/assets/src/ba_data/python/bastd/actor/onscreencountdown.py
index 59b5070c..6e767e54 100644
--- a/assets/src/ba_data/python/bastd/actor/onscreencountdown.py
+++ b/assets/src/ba_data/python/bastd/actor/onscreencountdown.py
@@ -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:
diff --git a/assets/src/ba_data/python/bastd/actor/onscreentimer.py b/assets/src/ba_data/python/bastd/actor/onscreentimer.py
index 8b5699b6..3facd7e3 100644
--- a/assets/src/ba_data/python/bastd/actor/onscreentimer.py
+++ b/assets/src/ba_data/python/bastd/actor/onscreentimer.py
@@ -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."""
diff --git a/assets/src/ba_data/python/bastd/actor/powerupbox.py b/assets/src/ba_data/python/bastd/actor/powerupbox.py
index 77dc6ab9..2af9a7fd 100644
--- a/assets/src/ba_data/python/bastd/actor/powerupbox.py
+++ b/assets/src/ba_data/python/bastd/actor/powerupbox.py
@@ -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.
diff --git a/assets/src/ba_data/python/bastd/actor/spaz.py b/assets/src/ba_data/python/bastd/actor/spaz.py
index cf710b07..5d769078 100644
--- a/assets/src/ba_data/python/bastd/actor/spaz.py
+++ b/assets/src/ba_data/python/bastd/actor/spaz.py
@@ -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,
diff --git a/assets/src/ba_data/python/bastd/actor/spazfactory.py b/assets/src/ba_data/python/bastd/actor/spazfactory.py
index ac7e8983..6946088a 100644
--- a/assets/src/ba_data/python/bastd/actor/spazfactory.py
+++ b/assets/src/ba_data/python/bastd/actor/spazfactory.py
@@ -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(
diff --git a/assets/src/ba_data/python/bastd/game/assault.py b/assets/src/ba_data/python/bastd/game/assault.py
index 062a030e..8e0bf524 100644
--- a/assets/src/ba_data/python/bastd/game/assault.py
+++ b/assets/src/ba_data/python/bastd/game/assault.py
@@ -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),
diff --git a/assets/src/ba_data/python/bastd/game/chosenone.py b/assets/src/ba_data/python/bastd/game/chosenone.py
index f65d12c8..740631bc 100644
--- a/assets/src/ba_data/python/bastd/game/chosenone.py
+++ b/assets/src/ba_data/python/bastd/game/chosenone.py
@@ -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)
diff --git a/assets/src/ba_data/python/bastd/game/conquest.py b/assets/src/ba_data/python/bastd/game/conquest.py
index c6ba9c4d..938b4c51 100644
--- a/assets/src/ba_data/python/bastd/game/conquest.py
+++ b/assets/src/ba_data/python/bastd/game/conquest.py
@@ -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)))
diff --git a/assets/src/ba_data/python/bastd/game/easteregghunt.py b/assets/src/ba_data/python/bastd/game/easteregghunt.py
index 244e79d4..31262e3a 100644
--- a/assets/src/ba_data/python/bastd/game/easteregghunt.py
+++ b/assets/src/ba_data/python/bastd/game/easteregghunt.py
@@ -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={
diff --git a/assets/src/ba_data/python/bastd/game/football.py b/assets/src/ba_data/python/bastd/game/football.py
index e0f5d7b0..d44573a7 100644
--- a/assets/src/ba_data/python/bastd/game/football.py
+++ b/assets/src/ba_data/python/bastd/game/football.py
@@ -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,
diff --git a/assets/src/ba_data/python/bastd/game/hockey.py b/assets/src/ba_data/python/bastd/game/hockey.py
index bfb6e196..333a5d19 100644
--- a/assets/src/ba_data/python/bastd/game/hockey.py
+++ b/assets/src/ba_data/python/bastd/game/hockey.py
@@ -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), ))
diff --git a/assets/src/ba_data/python/bastd/game/kingofthehill.py b/assets/src/ba_data/python/bastd/game/kingofthehill.py
index b0dfe47c..4b70add0 100644
--- a/assets/src/ba_data/python/bastd/game/kingofthehill.py
+++ b/assets/src/ba_data/python/bastd/game/kingofthehill.py
@@ -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,
diff --git a/assets/src/ba_data/python/bastd/game/race.py b/assets/src/ba_data/python/bastd/game/race.py
index 41485b2e..f28d2736 100644
--- a/assets/src/ba_data/python/bastd/game/race.py
+++ b/assets/src/ba_data/python/bastd/game/race.py
@@ -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)))
diff --git a/assets/src/ba_data/python/bastd/game/runaround.py b/assets/src/ba_data/python/bastd/game/runaround.py
index 7f6ca1d7..b268fe4f 100644
--- a/assets/src/ba_data/python/bastd/game/runaround.py
+++ b/assets/src/ba_data/python/bastd/game/runaround.py
@@ -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)))
diff --git a/assets/src/ba_data/python/bastd/game/targetpractice.py b/assets/src/ba_data/python/bastd/game/targetpractice.py
index ea34c785..0c4c0252 100644
--- a/assets/src/ba_data/python/bastd/game/targetpractice.py
+++ b/assets/src/ba_data/python/bastd/game/targetpractice.py
@@ -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)
diff --git a/assets/src/ba_data/python/bastd/gameutils.py b/assets/src/ba_data/python/bastd/gameutils.py
index 17cf39a8..4519e988 100644
--- a/assets/src/ba_data/python/bastd/gameutils.py
+++ b/assets/src/ba_data/python/bastd/gameutils.py
@@ -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
diff --git a/assets/src/ba_data/python/bastd/mainmenu.py b/assets/src/ba_data/python/bastd/mainmenu.py
index 391c082e..61cfb0f2 100644
--- a/assets/src/ba_data/python/bastd/mainmenu.py
+++ b/assets/src/ba_data/python/bastd/mainmenu.py
@@ -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)
diff --git a/assets/src/ba_data/python/bastd/maps.py b/assets/src/ba_data/python/bastd/maps.py
index 1e5158c6..29de16eb 100644
--- a/assets/src/ba_data/python/bastd/maps.py
+++ b/assets/src/ba_data/python/bastd/maps.py
@@ -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)
diff --git a/docs/ba_module.md b/docs/ba_module.md
index dbdf5458..75bccd57 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -1,5 +1,5 @@
-
last updated on 2020-05-28 for Ballistica version 1.5.0 build 20033
+last updated on 2020-05-29 for Ballistica version 1.5.0 build 20033
This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please let me know. Happy modding!
@@ -40,6 +40,7 @@
ba.SessionPlayer
ba.SessionTeam
+ ba.StandLocation
ba.Stats
ba.Team
ba.TeamGameResults
@@ -61,7 +62,6 @@
ba.playsound()
ba.printnodes()
ba.setmusic()
- ba.sharedobj()
ba.show_damage_count()
@@ -345,7 +345,7 @@ actually award achievements.
can overlap during transitions.
Attributes:
-
+
-
bool
@@ -355,6 +355,12 @@ actually award achievements.
At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.
+
+-
+
ba.Node
+The 'globals' ba.Node for the activity. This contains various
+ global controls and values.
+
-
List[PlayerType]
@@ -1541,7 +1547,7 @@ start_long_action(callback_when_done=ba.ContextC
Attributes Inherited:
Attributes Defined Here:
-
+
-
bool
@@ -1551,6 +1557,12 @@ start_long_action(callback_when_done=ba.ContextC
At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.
+
+-
+
ba.Node
+The 'globals' ba.Node for the activity. This contains various
+ global controls and values.
+
-
ba.Map
@@ -1660,12 +1672,18 @@ and it should begin its actual game logic.
Attributes Inherited:
Attributes Defined Here:
+
-
Optional[ba.Campaign]
The ba.Campaign instance this Session represents, or None if
there is no associated Campaign.
+
+-
+
ba.Node
+The sessionglobals ba.Node for the session.
+
Methods Inherited:
@@ -2011,6 +2029,14 @@ its time with lingering corpses, sound effects, etc.
Attributes Inherited:
+Attributes Defined Here:
+
+-
+
ba.Node
+The sessionglobals ba.Node for the session.
+
+
+
Methods Inherited:
Methods Defined or Overridden:
@@ -2049,6 +2075,14 @@ its time with lingering corpses, sound effects, etc.
Attributes Inherited:
+Attributes Defined Here:
+
+-
+
ba.Node
+The sessionglobals ba.Node for the session.
+
+
+
Methods Inherited:
Methods Defined or Overridden:
@@ -2098,7 +2132,7 @@ its time with lingering corpses, sound effects, etc.
Attributes Inherited:
Attributes Defined Here:
-
+
-
bool
@@ -2108,6 +2142,12 @@ its time with lingering corpses, sound effects, etc.
At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.
+
+-
+
ba.Node
+The 'globals' ba.Node for the activity. This contains various
+ global controls and values.
+
-
ba.Map
@@ -3299,7 +3339,7 @@ m.add_actions(actions=(('modify_part_collision', 'physical', False),
# 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)))
@@ -3329,6 +3369,14 @@ Use ba.getmodel() to instantiate one.
Attributes Inherited:
+Attributes Defined Here:
+
+-
+
ba.Node
+The sessionglobals ba.Node for the session.
+
+
+
Methods Inherited:
Methods Defined or Overridden:
@@ -4272,7 +4320,7 @@ Pass 0 or a negative number for no ban time.
maintaining state between them (players, teams, score tallies, etc).
Attributes:
-
+
-
bool
@@ -4305,6 +4353,11 @@ to proceed past the initial joining screen.
list in ba.Activity; not this. Some players, such as those who have
not yet selected a character, will only appear on this list.
+
+-
+
ba.Node
+The sessionglobals ba.Node for the session.
+
-
List[ba.SessionTeam]
@@ -4812,6 +4865,22 @@ of the session.
- MIKIROG
+<top level class>
+
+Describes a point in space and an angle to face.
+
+Category: Gameplay Classes
+
+
+Methods:
+
+-
+
ba.StandLocation(position: _ba.Vec3, angle: Optional[float] = None)
+
+
+
+
<top level class>
@@ -4961,7 +5030,7 @@ of the session.
Attributes Inherited:
Attributes Defined Here:
-
+
-
bool
@@ -4971,6 +5040,12 @@ of the session.
At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.
+
+-
+
ba.Node
+The 'globals' ba.Node for the activity. This contains various
+ global controls and values.
+
-
ba.Map
@@ -6303,54 +6378,6 @@ user can override particular game music with their own.
if 'continuous' is True and musictype is the same as what is already
playing, the playing track will not be restarted.
-
-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.
-
show_damage_count(damage: str, position: Sequence[float], direction: Sequence[float]) -> None