bascenev1lib.actor.spazfactory
Provides a factory object from creating Spazzes.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides a factory object from creating Spazzes.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING 8 9import bascenev1 as bs 10from bascenev1lib.gameutils import SharedObjects 11 12if TYPE_CHECKING: 13 from typing import Any, Sequence 14 15 16class SpazFactory: 17 """Wraps up media and other resources used by bs.Spaz instances. 18 19 Generally one of these is created per bascenev1.Activity and shared 20 between all spaz instances. Use bs.Spaz.get_factory() to return 21 the shared factory for the current activity. 22 """ 23 24 impact_sounds_medium: Sequence[bs.Sound] 25 """A tuple of bs.Sound-s for when a bs.Spaz hits something kinda hard.""" 26 27 impact_sounds_hard: Sequence[bs.Sound] 28 """A tuple of bs.Sound-s for when a bs.Spaz hits something really hard.""" 29 30 impact_sounds_harder: Sequence[bs.Sound] 31 """A tuple of bs.Sound-s for when a bs.Spaz hits something really 32 really hard.""" 33 34 single_player_death_sound: bs.Sound 35 """The sound that plays for an 'important' spaz death such as in 36 co-op games.""" 37 38 punch_sound_weak: bs.Sound 39 """A weak punch bs.Sound.""" 40 41 punch_sound: bs.Sound 42 """A standard punch bs.Sound.""" 43 44 punch_sound_strong: Sequence[bs.Sound] 45 """A tuple of stronger sounding punch bs.Sounds.""" 46 47 punch_sound_stronger: bs.Sound 48 """A really really strong sounding punch bs.Sound.""" 49 50 swish_sound: bs.Sound 51 """A punch swish bs.Sound.""" 52 53 block_sound: bs.Sound 54 """A bs.Sound for when an attack is blocked by invincibility.""" 55 56 shatter_sound: bs.Sound 57 """A bs.Sound for when a frozen bs.Spaz shatters.""" 58 59 splatter_sound: bs.Sound 60 """A bs.Sound for when a bs.Spaz blows up via curse.""" 61 62 spaz_material: bs.Material 63 """A bs.Material applied to all of parts of a bs.Spaz.""" 64 65 roller_material: bs.Material 66 """A bs.Material applied to the invisible roller ball body that 67 a bs.Spaz uses for locomotion.""" 68 69 punch_material: bs.Material 70 """A bs.Material applied to the 'fist' of a bs.Spaz.""" 71 72 pickup_material: bs.Material 73 """A bs.Material applied to the 'grabber' body of a bs.Spaz.""" 74 75 curse_material: bs.Material 76 """A bs.Material applied to a cursed bs.Spaz that triggers an explosion.""" 77 78 _STORENAME = bs.storagename() 79 80 def _preload(self, character: str) -> None: 81 """Preload media needed for a given character.""" 82 self.get_media(character) 83 84 def __init__(self) -> None: 85 """Instantiate a factory object.""" 86 # pylint: disable=cyclic-import 87 88 plus = bs.app.plus 89 assert plus is not None 90 91 # FIXME: should probably put these somewhere common so we don't 92 # have to import them from a module that imports us. 93 from bascenev1lib.actor.spaz import ( 94 PickupMessage, 95 PunchHitMessage, 96 CurseExplodeMessage, 97 ) 98 99 shared = SharedObjects.get() 100 self.impact_sounds_medium = ( 101 bs.getsound('impactMedium'), 102 bs.getsound('impactMedium2'), 103 ) 104 self.impact_sounds_hard = ( 105 bs.getsound('impactHard'), 106 bs.getsound('impactHard2'), 107 bs.getsound('impactHard3'), 108 ) 109 self.impact_sounds_harder = ( 110 bs.getsound('bigImpact'), 111 bs.getsound('bigImpact2'), 112 ) 113 self.single_player_death_sound = bs.getsound('playerDeath') 114 self.punch_sound_weak = bs.getsound('punchWeak01') 115 self.punch_sound = bs.getsound('punch01') 116 self.punch_sound_strong = ( 117 bs.getsound('punchStrong01'), 118 bs.getsound('punchStrong02'), 119 ) 120 self.punch_sound_stronger = bs.getsound('superPunch') 121 self.swish_sound = bs.getsound('punchSwish') 122 self.block_sound = bs.getsound('block') 123 self.shatter_sound = bs.getsound('shatter') 124 self.splatter_sound = bs.getsound('splatter') 125 self.spaz_material = bs.Material() 126 self.roller_material = bs.Material() 127 self.punch_material = bs.Material() 128 self.pickup_material = bs.Material() 129 self.curse_material = bs.Material() 130 131 footing_material = shared.footing_material 132 object_material = shared.object_material 133 player_material = shared.player_material 134 region_material = shared.region_material 135 136 # Send footing messages to spazzes so they know when they're on 137 # solid ground. 138 # Eww; this probably should just be built into the spaz node. 139 self.roller_material.add_actions( 140 conditions=('they_have_material', footing_material), 141 actions=( 142 ('message', 'our_node', 'at_connect', 'footing', 1), 143 ('message', 'our_node', 'at_disconnect', 'footing', -1), 144 ), 145 ) 146 147 self.spaz_material.add_actions( 148 conditions=('they_have_material', footing_material), 149 actions=( 150 ('message', 'our_node', 'at_connect', 'footing', 1), 151 ('message', 'our_node', 'at_disconnect', 'footing', -1), 152 ), 153 ) 154 155 # Punches. 156 self.punch_material.add_actions( 157 conditions=('they_are_different_node_than_us',), 158 actions=( 159 ('modify_part_collision', 'collide', True), 160 ('modify_part_collision', 'physical', False), 161 ('message', 'our_node', 'at_connect', PunchHitMessage()), 162 ), 163 ) 164 165 # Pickups. 166 self.pickup_material.add_actions( 167 conditions=( 168 ('they_are_different_node_than_us',), 169 'and', 170 ('they_have_material', object_material), 171 ), 172 actions=( 173 ('modify_part_collision', 'collide', True), 174 ('modify_part_collision', 'physical', False), 175 ('message', 'our_node', 'at_connect', PickupMessage()), 176 ), 177 ) 178 179 # Curse. 180 self.curse_material.add_actions( 181 conditions=( 182 ('they_are_different_node_than_us',), 183 'and', 184 ('they_have_material', player_material), 185 ), 186 actions=( 187 'message', 188 'our_node', 189 'at_connect', 190 CurseExplodeMessage(), 191 ), 192 ) 193 194 self.foot_impact_sounds = ( 195 bs.getsound('footImpact01'), 196 bs.getsound('footImpact02'), 197 bs.getsound('footImpact03'), 198 ) 199 200 self.foot_skid_sound = bs.getsound('skid01') 201 self.foot_roll_sound = bs.getsound('scamper01') 202 203 self.roller_material.add_actions( 204 conditions=('they_have_material', footing_material), 205 actions=( 206 ('impact_sound', self.foot_impact_sounds, 1, 0.2), 207 ('skid_sound', self.foot_skid_sound, 20, 0.3), 208 ('roll_sound', self.foot_roll_sound, 20, 3.0), 209 ), 210 ) 211 212 self.skid_sound = bs.getsound('gravelSkid') 213 214 self.spaz_material.add_actions( 215 conditions=('they_have_material', footing_material), 216 actions=( 217 ('impact_sound', self.foot_impact_sounds, 20, 6), 218 ('skid_sound', self.skid_sound, 2.0, 1), 219 ('roll_sound', self.skid_sound, 2.0, 1), 220 ), 221 ) 222 223 self.shield_up_sound = bs.getsound('shieldUp') 224 self.shield_down_sound = bs.getsound('shieldDown') 225 self.shield_hit_sound = bs.getsound('shieldHit') 226 227 # We don't want to collide with stuff we're initially overlapping 228 # (unless its marked with a special region material). 229 self.spaz_material.add_actions( 230 conditions=( 231 ( 232 ('we_are_younger_than', 51), 233 'and', 234 ('they_are_different_node_than_us',), 235 ), 236 'and', 237 ('they_dont_have_material', region_material), 238 ), 239 actions=('modify_node_collision', 'collide', False), 240 ) 241 242 self.spaz_media: dict[str, Any] = {} 243 244 # Lets load some basic rules. 245 # (allows them to be tweaked from the master server) 246 self.shield_decay_rate = plus.get_v1_account_misc_read_val('rsdr', 10.0) 247 self.punch_cooldown = plus.get_v1_account_misc_read_val('rpc', 400) 248 self.punch_cooldown_gloves = plus.get_v1_account_misc_read_val( 249 'rpcg', 300 250 ) 251 self.punch_power_scale = plus.get_v1_account_misc_read_val('rpp', 1.2) 252 self.punch_power_scale_gloves = plus.get_v1_account_misc_read_val( 253 'rppg', 1.4 254 ) 255 self.max_shield_spillover_damage = plus.get_v1_account_misc_read_val( 256 'rsms', 500 257 ) 258 259 def get_style(self, character: str) -> str: 260 """Return the named style for this character. 261 262 (this influences subtle aspects of their appearance, etc) 263 """ 264 assert bs.app.classic is not None 265 return bs.app.classic.spaz_appearances[character].style 266 267 def get_media(self, character: str) -> dict[str, Any]: 268 """Return the set of media used by this variant of spaz.""" 269 assert bs.app.classic is not None 270 char = bs.app.classic.spaz_appearances[character] 271 if character not in self.spaz_media: 272 media = self.spaz_media[character] = { 273 'jump_sounds': [bs.getsound(s) for s in char.jump_sounds], 274 'attack_sounds': [bs.getsound(s) for s in char.attack_sounds], 275 'impact_sounds': [bs.getsound(s) for s in char.impact_sounds], 276 'death_sounds': [bs.getsound(s) for s in char.death_sounds], 277 'pickup_sounds': [bs.getsound(s) for s in char.pickup_sounds], 278 'fall_sounds': [bs.getsound(s) for s in char.fall_sounds], 279 'color_texture': bs.gettexture(char.color_texture), 280 'color_mask_texture': bs.gettexture(char.color_mask_texture), 281 'head_mesh': bs.getmesh(char.head_mesh), 282 'torso_mesh': bs.getmesh(char.torso_mesh), 283 'pelvis_mesh': bs.getmesh(char.pelvis_mesh), 284 'upper_arm_mesh': bs.getmesh(char.upper_arm_mesh), 285 'forearm_mesh': bs.getmesh(char.forearm_mesh), 286 'hand_mesh': bs.getmesh(char.hand_mesh), 287 'upper_leg_mesh': bs.getmesh(char.upper_leg_mesh), 288 'lower_leg_mesh': bs.getmesh(char.lower_leg_mesh), 289 'toes_mesh': bs.getmesh(char.toes_mesh), 290 } 291 else: 292 media = self.spaz_media[character] 293 return media 294 295 @classmethod 296 def get(cls) -> SpazFactory: 297 """Return the shared bs.SpazFactory, creating it if necessary.""" 298 # pylint: disable=cyclic-import 299 activity = bs.getactivity() 300 factory = activity.customdata.get(cls._STORENAME) 301 if factory is None: 302 factory = activity.customdata[cls._STORENAME] = SpazFactory() 303 assert isinstance(factory, SpazFactory) 304 return factory
17class SpazFactory: 18 """Wraps up media and other resources used by bs.Spaz instances. 19 20 Generally one of these is created per bascenev1.Activity and shared 21 between all spaz instances. Use bs.Spaz.get_factory() to return 22 the shared factory for the current activity. 23 """ 24 25 impact_sounds_medium: Sequence[bs.Sound] 26 """A tuple of bs.Sound-s for when a bs.Spaz hits something kinda hard.""" 27 28 impact_sounds_hard: Sequence[bs.Sound] 29 """A tuple of bs.Sound-s for when a bs.Spaz hits something really hard.""" 30 31 impact_sounds_harder: Sequence[bs.Sound] 32 """A tuple of bs.Sound-s for when a bs.Spaz hits something really 33 really hard.""" 34 35 single_player_death_sound: bs.Sound 36 """The sound that plays for an 'important' spaz death such as in 37 co-op games.""" 38 39 punch_sound_weak: bs.Sound 40 """A weak punch bs.Sound.""" 41 42 punch_sound: bs.Sound 43 """A standard punch bs.Sound.""" 44 45 punch_sound_strong: Sequence[bs.Sound] 46 """A tuple of stronger sounding punch bs.Sounds.""" 47 48 punch_sound_stronger: bs.Sound 49 """A really really strong sounding punch bs.Sound.""" 50 51 swish_sound: bs.Sound 52 """A punch swish bs.Sound.""" 53 54 block_sound: bs.Sound 55 """A bs.Sound for when an attack is blocked by invincibility.""" 56 57 shatter_sound: bs.Sound 58 """A bs.Sound for when a frozen bs.Spaz shatters.""" 59 60 splatter_sound: bs.Sound 61 """A bs.Sound for when a bs.Spaz blows up via curse.""" 62 63 spaz_material: bs.Material 64 """A bs.Material applied to all of parts of a bs.Spaz.""" 65 66 roller_material: bs.Material 67 """A bs.Material applied to the invisible roller ball body that 68 a bs.Spaz uses for locomotion.""" 69 70 punch_material: bs.Material 71 """A bs.Material applied to the 'fist' of a bs.Spaz.""" 72 73 pickup_material: bs.Material 74 """A bs.Material applied to the 'grabber' body of a bs.Spaz.""" 75 76 curse_material: bs.Material 77 """A bs.Material applied to a cursed bs.Spaz that triggers an explosion.""" 78 79 _STORENAME = bs.storagename() 80 81 def _preload(self, character: str) -> None: 82 """Preload media needed for a given character.""" 83 self.get_media(character) 84 85 def __init__(self) -> None: 86 """Instantiate a factory object.""" 87 # pylint: disable=cyclic-import 88 89 plus = bs.app.plus 90 assert plus is not None 91 92 # FIXME: should probably put these somewhere common so we don't 93 # have to import them from a module that imports us. 94 from bascenev1lib.actor.spaz import ( 95 PickupMessage, 96 PunchHitMessage, 97 CurseExplodeMessage, 98 ) 99 100 shared = SharedObjects.get() 101 self.impact_sounds_medium = ( 102 bs.getsound('impactMedium'), 103 bs.getsound('impactMedium2'), 104 ) 105 self.impact_sounds_hard = ( 106 bs.getsound('impactHard'), 107 bs.getsound('impactHard2'), 108 bs.getsound('impactHard3'), 109 ) 110 self.impact_sounds_harder = ( 111 bs.getsound('bigImpact'), 112 bs.getsound('bigImpact2'), 113 ) 114 self.single_player_death_sound = bs.getsound('playerDeath') 115 self.punch_sound_weak = bs.getsound('punchWeak01') 116 self.punch_sound = bs.getsound('punch01') 117 self.punch_sound_strong = ( 118 bs.getsound('punchStrong01'), 119 bs.getsound('punchStrong02'), 120 ) 121 self.punch_sound_stronger = bs.getsound('superPunch') 122 self.swish_sound = bs.getsound('punchSwish') 123 self.block_sound = bs.getsound('block') 124 self.shatter_sound = bs.getsound('shatter') 125 self.splatter_sound = bs.getsound('splatter') 126 self.spaz_material = bs.Material() 127 self.roller_material = bs.Material() 128 self.punch_material = bs.Material() 129 self.pickup_material = bs.Material() 130 self.curse_material = bs.Material() 131 132 footing_material = shared.footing_material 133 object_material = shared.object_material 134 player_material = shared.player_material 135 region_material = shared.region_material 136 137 # Send footing messages to spazzes so they know when they're on 138 # solid ground. 139 # Eww; this probably should just be built into the spaz node. 140 self.roller_material.add_actions( 141 conditions=('they_have_material', footing_material), 142 actions=( 143 ('message', 'our_node', 'at_connect', 'footing', 1), 144 ('message', 'our_node', 'at_disconnect', 'footing', -1), 145 ), 146 ) 147 148 self.spaz_material.add_actions( 149 conditions=('they_have_material', footing_material), 150 actions=( 151 ('message', 'our_node', 'at_connect', 'footing', 1), 152 ('message', 'our_node', 'at_disconnect', 'footing', -1), 153 ), 154 ) 155 156 # Punches. 157 self.punch_material.add_actions( 158 conditions=('they_are_different_node_than_us',), 159 actions=( 160 ('modify_part_collision', 'collide', True), 161 ('modify_part_collision', 'physical', False), 162 ('message', 'our_node', 'at_connect', PunchHitMessage()), 163 ), 164 ) 165 166 # Pickups. 167 self.pickup_material.add_actions( 168 conditions=( 169 ('they_are_different_node_than_us',), 170 'and', 171 ('they_have_material', object_material), 172 ), 173 actions=( 174 ('modify_part_collision', 'collide', True), 175 ('modify_part_collision', 'physical', False), 176 ('message', 'our_node', 'at_connect', PickupMessage()), 177 ), 178 ) 179 180 # Curse. 181 self.curse_material.add_actions( 182 conditions=( 183 ('they_are_different_node_than_us',), 184 'and', 185 ('they_have_material', player_material), 186 ), 187 actions=( 188 'message', 189 'our_node', 190 'at_connect', 191 CurseExplodeMessage(), 192 ), 193 ) 194 195 self.foot_impact_sounds = ( 196 bs.getsound('footImpact01'), 197 bs.getsound('footImpact02'), 198 bs.getsound('footImpact03'), 199 ) 200 201 self.foot_skid_sound = bs.getsound('skid01') 202 self.foot_roll_sound = bs.getsound('scamper01') 203 204 self.roller_material.add_actions( 205 conditions=('they_have_material', footing_material), 206 actions=( 207 ('impact_sound', self.foot_impact_sounds, 1, 0.2), 208 ('skid_sound', self.foot_skid_sound, 20, 0.3), 209 ('roll_sound', self.foot_roll_sound, 20, 3.0), 210 ), 211 ) 212 213 self.skid_sound = bs.getsound('gravelSkid') 214 215 self.spaz_material.add_actions( 216 conditions=('they_have_material', footing_material), 217 actions=( 218 ('impact_sound', self.foot_impact_sounds, 20, 6), 219 ('skid_sound', self.skid_sound, 2.0, 1), 220 ('roll_sound', self.skid_sound, 2.0, 1), 221 ), 222 ) 223 224 self.shield_up_sound = bs.getsound('shieldUp') 225 self.shield_down_sound = bs.getsound('shieldDown') 226 self.shield_hit_sound = bs.getsound('shieldHit') 227 228 # We don't want to collide with stuff we're initially overlapping 229 # (unless its marked with a special region material). 230 self.spaz_material.add_actions( 231 conditions=( 232 ( 233 ('we_are_younger_than', 51), 234 'and', 235 ('they_are_different_node_than_us',), 236 ), 237 'and', 238 ('they_dont_have_material', region_material), 239 ), 240 actions=('modify_node_collision', 'collide', False), 241 ) 242 243 self.spaz_media: dict[str, Any] = {} 244 245 # Lets load some basic rules. 246 # (allows them to be tweaked from the master server) 247 self.shield_decay_rate = plus.get_v1_account_misc_read_val('rsdr', 10.0) 248 self.punch_cooldown = plus.get_v1_account_misc_read_val('rpc', 400) 249 self.punch_cooldown_gloves = plus.get_v1_account_misc_read_val( 250 'rpcg', 300 251 ) 252 self.punch_power_scale = plus.get_v1_account_misc_read_val('rpp', 1.2) 253 self.punch_power_scale_gloves = plus.get_v1_account_misc_read_val( 254 'rppg', 1.4 255 ) 256 self.max_shield_spillover_damage = plus.get_v1_account_misc_read_val( 257 'rsms', 500 258 ) 259 260 def get_style(self, character: str) -> str: 261 """Return the named style for this character. 262 263 (this influences subtle aspects of their appearance, etc) 264 """ 265 assert bs.app.classic is not None 266 return bs.app.classic.spaz_appearances[character].style 267 268 def get_media(self, character: str) -> dict[str, Any]: 269 """Return the set of media used by this variant of spaz.""" 270 assert bs.app.classic is not None 271 char = bs.app.classic.spaz_appearances[character] 272 if character not in self.spaz_media: 273 media = self.spaz_media[character] = { 274 'jump_sounds': [bs.getsound(s) for s in char.jump_sounds], 275 'attack_sounds': [bs.getsound(s) for s in char.attack_sounds], 276 'impact_sounds': [bs.getsound(s) for s in char.impact_sounds], 277 'death_sounds': [bs.getsound(s) for s in char.death_sounds], 278 'pickup_sounds': [bs.getsound(s) for s in char.pickup_sounds], 279 'fall_sounds': [bs.getsound(s) for s in char.fall_sounds], 280 'color_texture': bs.gettexture(char.color_texture), 281 'color_mask_texture': bs.gettexture(char.color_mask_texture), 282 'head_mesh': bs.getmesh(char.head_mesh), 283 'torso_mesh': bs.getmesh(char.torso_mesh), 284 'pelvis_mesh': bs.getmesh(char.pelvis_mesh), 285 'upper_arm_mesh': bs.getmesh(char.upper_arm_mesh), 286 'forearm_mesh': bs.getmesh(char.forearm_mesh), 287 'hand_mesh': bs.getmesh(char.hand_mesh), 288 'upper_leg_mesh': bs.getmesh(char.upper_leg_mesh), 289 'lower_leg_mesh': bs.getmesh(char.lower_leg_mesh), 290 'toes_mesh': bs.getmesh(char.toes_mesh), 291 } 292 else: 293 media = self.spaz_media[character] 294 return media 295 296 @classmethod 297 def get(cls) -> SpazFactory: 298 """Return the shared bs.SpazFactory, creating it if necessary.""" 299 # pylint: disable=cyclic-import 300 activity = bs.getactivity() 301 factory = activity.customdata.get(cls._STORENAME) 302 if factory is None: 303 factory = activity.customdata[cls._STORENAME] = SpazFactory() 304 assert isinstance(factory, SpazFactory) 305 return factory
Wraps up media and other resources used by bs.Spaz instances.
Generally one of these is created per bascenev1.Activity and shared between all spaz instances. Use bs.Spaz.get_factory() to return the shared factory for the current activity.
85 def __init__(self) -> None: 86 """Instantiate a factory object.""" 87 # pylint: disable=cyclic-import 88 89 plus = bs.app.plus 90 assert plus is not None 91 92 # FIXME: should probably put these somewhere common so we don't 93 # have to import them from a module that imports us. 94 from bascenev1lib.actor.spaz import ( 95 PickupMessage, 96 PunchHitMessage, 97 CurseExplodeMessage, 98 ) 99 100 shared = SharedObjects.get() 101 self.impact_sounds_medium = ( 102 bs.getsound('impactMedium'), 103 bs.getsound('impactMedium2'), 104 ) 105 self.impact_sounds_hard = ( 106 bs.getsound('impactHard'), 107 bs.getsound('impactHard2'), 108 bs.getsound('impactHard3'), 109 ) 110 self.impact_sounds_harder = ( 111 bs.getsound('bigImpact'), 112 bs.getsound('bigImpact2'), 113 ) 114 self.single_player_death_sound = bs.getsound('playerDeath') 115 self.punch_sound_weak = bs.getsound('punchWeak01') 116 self.punch_sound = bs.getsound('punch01') 117 self.punch_sound_strong = ( 118 bs.getsound('punchStrong01'), 119 bs.getsound('punchStrong02'), 120 ) 121 self.punch_sound_stronger = bs.getsound('superPunch') 122 self.swish_sound = bs.getsound('punchSwish') 123 self.block_sound = bs.getsound('block') 124 self.shatter_sound = bs.getsound('shatter') 125 self.splatter_sound = bs.getsound('splatter') 126 self.spaz_material = bs.Material() 127 self.roller_material = bs.Material() 128 self.punch_material = bs.Material() 129 self.pickup_material = bs.Material() 130 self.curse_material = bs.Material() 131 132 footing_material = shared.footing_material 133 object_material = shared.object_material 134 player_material = shared.player_material 135 region_material = shared.region_material 136 137 # Send footing messages to spazzes so they know when they're on 138 # solid ground. 139 # Eww; this probably should just be built into the spaz node. 140 self.roller_material.add_actions( 141 conditions=('they_have_material', footing_material), 142 actions=( 143 ('message', 'our_node', 'at_connect', 'footing', 1), 144 ('message', 'our_node', 'at_disconnect', 'footing', -1), 145 ), 146 ) 147 148 self.spaz_material.add_actions( 149 conditions=('they_have_material', footing_material), 150 actions=( 151 ('message', 'our_node', 'at_connect', 'footing', 1), 152 ('message', 'our_node', 'at_disconnect', 'footing', -1), 153 ), 154 ) 155 156 # Punches. 157 self.punch_material.add_actions( 158 conditions=('they_are_different_node_than_us',), 159 actions=( 160 ('modify_part_collision', 'collide', True), 161 ('modify_part_collision', 'physical', False), 162 ('message', 'our_node', 'at_connect', PunchHitMessage()), 163 ), 164 ) 165 166 # Pickups. 167 self.pickup_material.add_actions( 168 conditions=( 169 ('they_are_different_node_than_us',), 170 'and', 171 ('they_have_material', object_material), 172 ), 173 actions=( 174 ('modify_part_collision', 'collide', True), 175 ('modify_part_collision', 'physical', False), 176 ('message', 'our_node', 'at_connect', PickupMessage()), 177 ), 178 ) 179 180 # Curse. 181 self.curse_material.add_actions( 182 conditions=( 183 ('they_are_different_node_than_us',), 184 'and', 185 ('they_have_material', player_material), 186 ), 187 actions=( 188 'message', 189 'our_node', 190 'at_connect', 191 CurseExplodeMessage(), 192 ), 193 ) 194 195 self.foot_impact_sounds = ( 196 bs.getsound('footImpact01'), 197 bs.getsound('footImpact02'), 198 bs.getsound('footImpact03'), 199 ) 200 201 self.foot_skid_sound = bs.getsound('skid01') 202 self.foot_roll_sound = bs.getsound('scamper01') 203 204 self.roller_material.add_actions( 205 conditions=('they_have_material', footing_material), 206 actions=( 207 ('impact_sound', self.foot_impact_sounds, 1, 0.2), 208 ('skid_sound', self.foot_skid_sound, 20, 0.3), 209 ('roll_sound', self.foot_roll_sound, 20, 3.0), 210 ), 211 ) 212 213 self.skid_sound = bs.getsound('gravelSkid') 214 215 self.spaz_material.add_actions( 216 conditions=('they_have_material', footing_material), 217 actions=( 218 ('impact_sound', self.foot_impact_sounds, 20, 6), 219 ('skid_sound', self.skid_sound, 2.0, 1), 220 ('roll_sound', self.skid_sound, 2.0, 1), 221 ), 222 ) 223 224 self.shield_up_sound = bs.getsound('shieldUp') 225 self.shield_down_sound = bs.getsound('shieldDown') 226 self.shield_hit_sound = bs.getsound('shieldHit') 227 228 # We don't want to collide with stuff we're initially overlapping 229 # (unless its marked with a special region material). 230 self.spaz_material.add_actions( 231 conditions=( 232 ( 233 ('we_are_younger_than', 51), 234 'and', 235 ('they_are_different_node_than_us',), 236 ), 237 'and', 238 ('they_dont_have_material', region_material), 239 ), 240 actions=('modify_node_collision', 'collide', False), 241 ) 242 243 self.spaz_media: dict[str, Any] = {} 244 245 # Lets load some basic rules. 246 # (allows them to be tweaked from the master server) 247 self.shield_decay_rate = plus.get_v1_account_misc_read_val('rsdr', 10.0) 248 self.punch_cooldown = plus.get_v1_account_misc_read_val('rpc', 400) 249 self.punch_cooldown_gloves = plus.get_v1_account_misc_read_val( 250 'rpcg', 300 251 ) 252 self.punch_power_scale = plus.get_v1_account_misc_read_val('rpp', 1.2) 253 self.punch_power_scale_gloves = plus.get_v1_account_misc_read_val( 254 'rppg', 1.4 255 ) 256 self.max_shield_spillover_damage = plus.get_v1_account_misc_read_val( 257 'rsms', 500 258 )
Instantiate a factory object.
A tuple of bs.Sound-s for when a bs.Spaz hits something kinda hard.
A tuple of bs.Sound-s for when a bs.Spaz hits something really hard.
A tuple of bs.Sound-s for when a bs.Spaz hits something really really hard.
The sound that plays for an 'important' spaz death such as in co-op games.
A bs.Material applied to the invisible roller ball body that a bs.Spaz uses for locomotion.
A bs.Material applied to a cursed bs.Spaz that triggers an explosion.
260 def get_style(self, character: str) -> str: 261 """Return the named style for this character. 262 263 (this influences subtle aspects of their appearance, etc) 264 """ 265 assert bs.app.classic is not None 266 return bs.app.classic.spaz_appearances[character].style
Return the named style for this character.
(this influences subtle aspects of their appearance, etc)
268 def get_media(self, character: str) -> dict[str, Any]: 269 """Return the set of media used by this variant of spaz.""" 270 assert bs.app.classic is not None 271 char = bs.app.classic.spaz_appearances[character] 272 if character not in self.spaz_media: 273 media = self.spaz_media[character] = { 274 'jump_sounds': [bs.getsound(s) for s in char.jump_sounds], 275 'attack_sounds': [bs.getsound(s) for s in char.attack_sounds], 276 'impact_sounds': [bs.getsound(s) for s in char.impact_sounds], 277 'death_sounds': [bs.getsound(s) for s in char.death_sounds], 278 'pickup_sounds': [bs.getsound(s) for s in char.pickup_sounds], 279 'fall_sounds': [bs.getsound(s) for s in char.fall_sounds], 280 'color_texture': bs.gettexture(char.color_texture), 281 'color_mask_texture': bs.gettexture(char.color_mask_texture), 282 'head_mesh': bs.getmesh(char.head_mesh), 283 'torso_mesh': bs.getmesh(char.torso_mesh), 284 'pelvis_mesh': bs.getmesh(char.pelvis_mesh), 285 'upper_arm_mesh': bs.getmesh(char.upper_arm_mesh), 286 'forearm_mesh': bs.getmesh(char.forearm_mesh), 287 'hand_mesh': bs.getmesh(char.hand_mesh), 288 'upper_leg_mesh': bs.getmesh(char.upper_leg_mesh), 289 'lower_leg_mesh': bs.getmesh(char.lower_leg_mesh), 290 'toes_mesh': bs.getmesh(char.toes_mesh), 291 } 292 else: 293 media = self.spaz_media[character] 294 return media
Return the set of media used by this variant of spaz.
296 @classmethod 297 def get(cls) -> SpazFactory: 298 """Return the shared bs.SpazFactory, creating it if necessary.""" 299 # pylint: disable=cyclic-import 300 activity = bs.getactivity() 301 factory = activity.customdata.get(cls._STORENAME) 302 if factory is None: 303 factory = activity.customdata[cls._STORENAME] = SpazFactory() 304 assert isinstance(factory, SpazFactory) 305 return factory
Return the shared bs.SpazFactory, creating it if necessary.