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('Use SharedObjects.get() to fetch the' 31 ' shared instance for this activity.') 32 self._object_material: ba.Material | None = None 33 self._player_material: ba.Material | None = None 34 self._pickup_material: ba.Material | None = None 35 self._footing_material: ba.Material | None = None 36 self._attack_material: ba.Material | None = None 37 self._death_material: ba.Material | None = None 38 self._region_material: ba.Material | None = None 39 self._railing_material: ba.Material | None = None 40 41 @classmethod 42 def get(cls) -> SharedObjects: 43 """Fetch/create the instance of this class for the current activity.""" 44 activity = ba.getactivity() 45 shobs = activity.customdata.get(cls._STORENAME) 46 if shobs is None: 47 shobs = SharedObjects() 48 activity.customdata[cls._STORENAME] = shobs 49 assert isinstance(shobs, SharedObjects) 50 return shobs 51 52 @property 53 def player_material(self) -> ba.Material: 54 """a ba.Material to be applied to player parts. Generally, 55 materials related to the process of scoring when reaching a goal, etc 56 will look for the presence of this material on things that hit them. 57 """ 58 if self._player_material is None: 59 self._player_material = ba.Material() 60 return self._player_material 61 62 @property 63 def object_material(self) -> ba.Material: 64 """A ba.Material that should be applied to any small, 65 normal, physical objects such as bombs, boxes, players, etc. Other 66 materials often check for the presence of this material as a 67 prerequisite for performing certain actions (such as disabling 68 collisions between initially-overlapping objects) 69 """ 70 if self._object_material is None: 71 self._object_material = ba.Material() 72 return self._object_material 73 74 @property 75 def pickup_material(self) -> ba.Material: 76 """A ba.Material; collision shapes used for picking things 77 up will have this material applied. To prevent an object from being 78 picked up, you can add a material that disables collisions against 79 things containing this material. 80 """ 81 if self._pickup_material is None: 82 self._pickup_material = ba.Material() 83 return self._pickup_material 84 85 @property 86 def footing_material(self) -> ba.Material: 87 """Anything that can be 'walked on' should have this 88 ba.Material applied; generally just terrain and whatnot. A character 89 will snap upright whenever touching something with this material so it 90 should not be applied to props, etc. 91 """ 92 if self._footing_material is None: 93 self._footing_material = ba.Material() 94 return self._footing_material 95 96 @property 97 def attack_material(self) -> ba.Material: 98 """A ba.Material applied to explosion shapes, punch 99 shapes, etc. An object not wanting to receive impulse/etc messages can 100 disable collisions against this material. 101 """ 102 if self._attack_material is None: 103 self._attack_material = ba.Material() 104 return self._attack_material 105 106 @property 107 def death_material(self) -> ba.Material: 108 """A ba.Material that sends a ba.DieMessage() to anything 109 that touches it; handy for terrain below a cliff, etc. 110 """ 111 if self._death_material is None: 112 mat = self._death_material = ba.Material() 113 mat.add_actions( 114 ('message', 'their_node', 'at_connect', ba.DieMessage())) 115 return self._death_material 116 117 @property 118 def region_material(self) -> ba.Material: 119 """A ba.Material used for non-physical collision shapes 120 (regions); collisions can generally be allowed with this material even 121 when initially overlapping since it is not physical. 122 """ 123 if self._region_material is None: 124 self._region_material = ba.Material() 125 return self._region_material 126 127 @property 128 def railing_material(self) -> ba.Material: 129 """A ba.Material with a very low friction/stiffness/etc 130 that can be applied to invisible 'railings' useful for gently keeping 131 characters from falling off of cliffs. 132 """ 133 if self._railing_material is None: 134 mat = self._railing_material = ba.Material() 135 mat.add_actions(('modify_part_collision', 'collide', False)) 136 mat.add_actions(('modify_part_collision', 'stiffness', 0.003)) 137 mat.add_actions(('modify_part_collision', 'damping', 0.00001)) 138 mat.add_actions( 139 conditions=('they_have_material', self.player_material), 140 actions=( 141 ('modify_part_collision', 'collide', True), 142 ('modify_part_collision', 'friction', 0.0), 143 ), 144 ) 145 return self._railing_material