bascenev1lib.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 bascenev1 as bs 10 11if TYPE_CHECKING: 12 pass 13 14 15class SharedObjects: 16 """Various common components for use in games. 17 18 Objects contained here are created on-demand as accessed and shared 19 by everything in the current activity. This includes things such as 20 standard materials. 21 """ 22 23 _STORENAME = bs.storagename() 24 25 def __init__(self) -> None: 26 activity = bs.getactivity() 27 if self._STORENAME in activity.customdata: 28 raise RuntimeError( 29 'Use SharedObjects.get() to fetch the' 30 ' shared instance for this activity.' 31 ) 32 self._object_material: bs.Material | None = None 33 self._player_material: bs.Material | None = None 34 self._pickup_material: bs.Material | None = None 35 self._footing_material: bs.Material | None = None 36 self._attack_material: bs.Material | None = None 37 self._death_material: bs.Material | None = None 38 self._region_material: bs.Material | None = None 39 self._railing_material: bs.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 = bs.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) -> bs.Material: 54 """a bascenev1.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 = bs.Material() 60 return self._player_material 61 62 @property 63 def object_material(self) -> bs.Material: 64 """A bascenev1.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 = bs.Material() 72 return self._object_material 73 74 @property 75 def pickup_material(self) -> bs.Material: 76 """A bascenev1.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 = bs.Material() 83 return self._pickup_material 84 85 @property 86 def footing_material(self) -> bs.Material: 87 """Anything that can be 'walked on' should have this 88 bascenev1.Material applied; generally just terrain and whatnot. 89 A character will snap upright whenever touching something with this 90 material so it should not be applied to props, etc. 91 """ 92 if self._footing_material is None: 93 self._footing_material = bs.Material() 94 return self._footing_material 95 96 @property 97 def attack_material(self) -> bs.Material: 98 """A bascenev1.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 = bs.Material() 104 return self._attack_material 105 106 @property 107 def death_material(self) -> bs.Material: 108 """A bascenev1.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 = bs.Material() 113 mat.add_actions( 114 ('message', 'their_node', 'at_connect', bs.DieMessage()) 115 ) 116 return self._death_material 117 118 @property 119 def region_material(self) -> bs.Material: 120 """A bascenev1.Material used for non-physical collision shapes 121 (regions); collisions can generally be allowed with this material even 122 when initially overlapping since it is not physical. 123 """ 124 if self._region_material is None: 125 self._region_material = bs.Material() 126 return self._region_material 127 128 @property 129 def railing_material(self) -> bs.Material: 130 """A bascenev1.Material with a very low friction/stiffness/etc 131 that can be applied to invisible 'railings' useful for gently keeping 132 characters from falling off of cliffs. 133 """ 134 if self._railing_material is None: 135 mat = self._railing_material = bs.Material() 136 mat.add_actions(('modify_part_collision', 'collide', False)) 137 mat.add_actions(('modify_part_collision', 'stiffness', 0.003)) 138 mat.add_actions(('modify_part_collision', 'damping', 0.00001)) 139 mat.add_actions( 140 conditions=('they_have_material', self.player_material), 141 actions=( 142 ('modify_part_collision', 'collide', True), 143 ('modify_part_collision', 'friction', 0.0), 144 ), 145 ) 146 return self._railing_material