bastd.gameutils
Various utilities useful for gameplay.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Various utilities useful for gameplay.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING 8 9import ba 10 11if TYPE_CHECKING: 12 pass 13 14 15class SharedObjects: 16 """Various common components for use in games. 17 18 Category: Gameplay Classes 19 20 Objects contained here are created on-demand as accessed and shared 21 by everything in the current activity. This includes things such as 22 standard materials. 23 """ 24 25 _STORENAME = ba.storagename() 26 27 def __init__(self) -> None: 28 activity = ba.getactivity() 29 if self._STORENAME in activity.customdata: 30 raise RuntimeError( 31 'Use SharedObjects.get() to fetch the' 32 ' shared instance for this activity.' 33 ) 34 self._object_material: ba.Material | None = None 35 self._player_material: ba.Material | None = None 36 self._pickup_material: ba.Material | None = None 37 self._footing_material: ba.Material | None = None 38 self._attack_material: ba.Material | None = None 39 self._death_material: ba.Material | None = None 40 self._region_material: ba.Material | None = None 41 self._railing_material: ba.Material | None = None 42 43 @classmethod 44 def get(cls) -> SharedObjects: 45 """Fetch/create the instance of this class for the current activity.""" 46 activity = ba.getactivity() 47 shobs = activity.customdata.get(cls._STORENAME) 48 if shobs is None: 49 shobs = SharedObjects() 50 activity.customdata[cls._STORENAME] = shobs 51 assert isinstance(shobs, SharedObjects) 52 return shobs 53 54 @property 55 def player_material(self) -> ba.Material: 56 """a ba.Material to be applied to player parts. Generally, 57 materials related to the process of scoring when reaching a goal, etc 58 will look for the presence of this material on things that hit them. 59 """ 60 if self._player_material is None: 61 self._player_material = ba.Material() 62 return self._player_material 63 64 @property 65 def object_material(self) -> ba.Material: 66 """A ba.Material that should be applied to any small, 67 normal, physical objects such as bombs, boxes, players, etc. Other 68 materials often check for the presence of this material as a 69 prerequisite for performing certain actions (such as disabling 70 collisions between initially-overlapping objects) 71 """ 72 if self._object_material is None: 73 self._object_material = ba.Material() 74 return self._object_material 75 76 @property 77 def pickup_material(self) -> ba.Material: 78 """A ba.Material; collision shapes used for picking things 79 up will have this material applied. To prevent an object from being 80 picked up, you can add a material that disables collisions against 81 things containing this material. 82 """ 83 if self._pickup_material is None: 84 self._pickup_material = ba.Material() 85 return self._pickup_material 86 87 @property 88 def footing_material(self) -> ba.Material: 89 """Anything that can be 'walked on' should have this 90 ba.Material applied; generally just terrain and whatnot. A character 91 will snap upright whenever touching something with this material so it 92 should not be applied to props, etc. 93 """ 94 if self._footing_material is None: 95 self._footing_material = ba.Material() 96 return self._footing_material 97 98 @property 99 def attack_material(self) -> ba.Material: 100 """A ba.Material applied to explosion shapes, punch 101 shapes, etc. An object not wanting to receive impulse/etc messages can 102 disable collisions against this material. 103 """ 104 if self._attack_material is None: 105 self._attack_material = ba.Material() 106 return self._attack_material 107 108 @property 109 def death_material(self) -> ba.Material: 110 """A ba.Material that sends a ba.DieMessage() to anything 111 that touches it; handy for terrain below a cliff, etc. 112 """ 113 if self._death_material is None: 114 mat = self._death_material = ba.Material() 115 mat.add_actions( 116 ('message', 'their_node', 'at_connect', ba.DieMessage()) 117 ) 118 return self._death_material 119 120 @property 121 def region_material(self) -> ba.Material: 122 """A ba.Material used for non-physical collision shapes 123 (regions); collisions can generally be allowed with this material even 124 when initially overlapping since it is not physical. 125 """ 126 if self._region_material is None: 127 self._region_material = ba.Material() 128 return self._region_material 129 130 @property 131 def railing_material(self) -> ba.Material: 132 """A ba.Material with a very low friction/stiffness/etc 133 that can be applied to invisible 'railings' useful for gently keeping 134 characters from falling off of cliffs. 135 """ 136 if self._railing_material is None: 137 mat = self._railing_material = ba.Material() 138 mat.add_actions(('modify_part_collision', 'collide', False)) 139 mat.add_actions(('modify_part_collision', 'stiffness', 0.003)) 140 mat.add_actions(('modify_part_collision', 'damping', 0.00001)) 141 mat.add_actions( 142 conditions=('they_have_material', self.player_material), 143 actions=( 144 ('modify_part_collision', 'collide', True), 145 ('modify_part_collision', 'friction', 0.0), 146 ), 147 ) 148 return self._railing_material