# Released under the MIT License. See LICENSE for details. # """Various utilities useful for gameplay.""" from __future__ import annotations from typing import TYPE_CHECKING import ba if TYPE_CHECKING: from typing import Sequence, Optional 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. """ _STORENAME = ba.storagename() def __init__(self) -> None: activity = ba.getactivity() if hasattr(activity, self._STORENAME): 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 @classmethod def get(cls) -> SharedObjects: """Fetch/create the instance of this class for the current activity.""" activity = ba.getactivity() shobs = activity.customdata.get(cls._STORENAME) if shobs is None: shobs = SharedObjects() activity.customdata[cls._STORENAME] = 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