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