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

Wraps up media and other resources used by ba.Spaz instances.

Category: Gameplay Classes

Generally one of these is created per ba.Activity and shared between all spaz instances. Use ba.Spaz.get_factory() to return the shared factory for the current activity.

SpazFactory()
 85    def __init__(self) -> None:
 86        """Instantiate a factory object."""
 87        # pylint: disable=cyclic-import
 88        # FIXME: should probably put these somewhere common so we don't
 89        # have to import them from a module that imports us.
 90        from bastd.actor.spaz import (PickupMessage, PunchHitMessage,
 91                                      CurseExplodeMessage)
 92
 93        shared = SharedObjects.get()
 94        self.impact_sounds_medium = (ba.getsound('impactMedium'),
 95                                     ba.getsound('impactMedium2'))
 96        self.impact_sounds_hard = (ba.getsound('impactHard'),
 97                                   ba.getsound('impactHard2'),
 98                                   ba.getsound('impactHard3'))
 99        self.impact_sounds_harder = (ba.getsound('bigImpact'),
100                                     ba.getsound('bigImpact2'))
101        self.single_player_death_sound = ba.getsound('playerDeath')
102        self.punch_sound = ba.getsound('punch01')
103        self.punch_sound_strong = (ba.getsound('punchStrong01'),
104                                   ba.getsound('punchStrong02'))
105        self.punch_sound_stronger = ba.getsound('superPunch')
106        self.swish_sound = ba.getsound('punchSwish')
107        self.block_sound = ba.getsound('block')
108        self.shatter_sound = ba.getsound('shatter')
109        self.splatter_sound = ba.getsound('splatter')
110        self.spaz_material = ba.Material()
111        self.roller_material = ba.Material()
112        self.punch_material = ba.Material()
113        self.pickup_material = ba.Material()
114        self.curse_material = ba.Material()
115
116        footing_material = shared.footing_material
117        object_material = shared.object_material
118        player_material = shared.player_material
119        region_material = shared.region_material
120
121        # Send footing messages to spazzes so they know when they're on
122        # solid ground.
123        # Eww; this probably should just be built into the spaz node.
124        self.roller_material.add_actions(
125            conditions=('they_have_material', footing_material),
126            actions=(('message', 'our_node', 'at_connect', 'footing', 1),
127                     ('message', 'our_node', 'at_disconnect', 'footing', -1)))
128
129        self.spaz_material.add_actions(
130            conditions=('they_have_material', footing_material),
131            actions=(('message', 'our_node', 'at_connect', 'footing', 1),
132                     ('message', 'our_node', 'at_disconnect', 'footing', -1)))
133
134        # Punches.
135        self.punch_material.add_actions(
136            conditions=('they_are_different_node_than_us', ),
137            actions=(
138                ('modify_part_collision', 'collide', True),
139                ('modify_part_collision', 'physical', False),
140                ('message', 'our_node', 'at_connect', PunchHitMessage()),
141            ))
142
143        # Pickups.
144        self.pickup_material.add_actions(
145            conditions=(('they_are_different_node_than_us', ), 'and',
146                        ('they_have_material', object_material)),
147            actions=(
148                ('modify_part_collision', 'collide', True),
149                ('modify_part_collision', 'physical', False),
150                ('message', 'our_node', 'at_connect', PickupMessage()),
151            ))
152
153        # Curse.
154        self.curse_material.add_actions(
155            conditions=(
156                ('they_are_different_node_than_us', ),
157                'and',
158                ('they_have_material', player_material),
159            ),
160            actions=('message', 'our_node', 'at_connect',
161                     CurseExplodeMessage()),
162        )
163
164        self.foot_impact_sounds = (ba.getsound('footImpact01'),
165                                   ba.getsound('footImpact02'),
166                                   ba.getsound('footImpact03'))
167
168        self.foot_skid_sound = ba.getsound('skid01')
169        self.foot_roll_sound = ba.getsound('scamper01')
170
171        self.roller_material.add_actions(
172            conditions=('they_have_material', footing_material),
173            actions=(
174                ('impact_sound', self.foot_impact_sounds, 1, 0.2),
175                ('skid_sound', self.foot_skid_sound, 20, 0.3),
176                ('roll_sound', self.foot_roll_sound, 20, 3.0),
177            ))
178
179        self.skid_sound = ba.getsound('gravelSkid')
180
181        self.spaz_material.add_actions(
182            conditions=('they_have_material', footing_material),
183            actions=(
184                ('impact_sound', self.foot_impact_sounds, 20, 6),
185                ('skid_sound', self.skid_sound, 2.0, 1),
186                ('roll_sound', self.skid_sound, 2.0, 1),
187            ))
188
189        self.shield_up_sound = ba.getsound('shieldUp')
190        self.shield_down_sound = ba.getsound('shieldDown')
191        self.shield_hit_sound = ba.getsound('shieldHit')
192
193        # We don't want to collide with stuff we're initially overlapping
194        # (unless its marked with a special region material).
195        self.spaz_material.add_actions(
196            conditions=(
197                (
198                    ('we_are_younger_than', 51),
199                    'and',
200                    ('they_are_different_node_than_us', ),
201                ),
202                'and',
203                ('they_dont_have_material', region_material),
204            ),
205            actions=('modify_node_collision', 'collide', False),
206        )
207
208        self.spaz_media: dict[str, Any] = {}
209
210        # Lets load some basic rules.
211        # (allows them to be tweaked from the master server)
212        self.shield_decay_rate = _ba.get_v1_account_misc_read_val('rsdr', 10.0)
213        self.punch_cooldown = _ba.get_v1_account_misc_read_val('rpc', 400)
214        self.punch_cooldown_gloves = (_ba.get_v1_account_misc_read_val(
215            'rpcg', 300))
216        self.punch_power_scale = _ba.get_v1_account_misc_read_val('rpp', 1.2)
217        self.punch_power_scale_gloves = (_ba.get_v1_account_misc_read_val(
218            'rppg', 1.4))
219        self.max_shield_spillover_damage = (_ba.get_v1_account_misc_read_val(
220            'rsms', 500))

Instantiate a factory object.

impact_sounds_medium: Sequence[_ba.Sound]

A tuple of ba.Sound-s for when a ba.Spaz hits something kinda hard.

impact_sounds_hard: Sequence[_ba.Sound]

A tuple of ba.Sound-s for when a ba.Spaz hits something really hard.

impact_sounds_harder: Sequence[_ba.Sound]

A tuple of ba.Sound-s for when a ba.Spaz hits something really really hard.

single_player_death_sound: _ba.Sound

The sound that plays for an 'important' spaz death such as in co-op games.

punch_sound: _ba.Sound

A standard punch ba.Sound.

punch_sound_strong: Sequence[_ba.Sound]

A tuple of stronger sounding punch ba.Sounds.

punch_sound_stronger: _ba.Sound

A really really strong sounding punch ba.Sound.

swish_sound: _ba.Sound

A punch swish ba.Sound.

block_sound: _ba.Sound

A ba.Sound for when an attack is blocked by invincibility.

shatter_sound: _ba.Sound

A ba.Sound for when a frozen ba.Spaz shatters.

splatter_sound: _ba.Sound

A ba.Sound for when a ba.Spaz blows up via curse.

spaz_material: _ba.Material

A ba.Material applied to all of parts of a ba.Spaz.

roller_material: _ba.Material

A ba.Material applied to the invisible roller ball body that a ba.Spaz uses for locomotion.

punch_material: _ba.Material

A ba.Material applied to the 'fist' of a ba.Spaz.

pickup_material: _ba.Material

A ba.Material applied to the 'grabber' body of a ba.Spaz.

curse_material: _ba.Material

A ba.Material applied to a cursed ba.Spaz that triggers an explosion.

def get_style(self, character: str) -> str:
222    def get_style(self, character: str) -> str:
223        """Return the named style for this character.
224
225        (this influences subtle aspects of their appearance, etc)
226        """
227        return ba.app.spaz_appearances[character].style

Return the named style for this character.

(this influences subtle aspects of their appearance, etc)

def get_media(self, character: str) -> dict[str, typing.Any]:
229    def get_media(self, character: str) -> dict[str, Any]:
230        """Return the set of media used by this variant of spaz."""
231        char = ba.app.spaz_appearances[character]
232        if character not in self.spaz_media:
233            media = self.spaz_media[character] = {
234                'jump_sounds': [ba.getsound(s) for s in char.jump_sounds],
235                'attack_sounds': [ba.getsound(s) for s in char.attack_sounds],
236                'impact_sounds': [ba.getsound(s) for s in char.impact_sounds],
237                'death_sounds': [ba.getsound(s) for s in char.death_sounds],
238                'pickup_sounds': [ba.getsound(s) for s in char.pickup_sounds],
239                'fall_sounds': [ba.getsound(s) for s in char.fall_sounds],
240                'color_texture': ba.gettexture(char.color_texture),
241                'color_mask_texture': ba.gettexture(char.color_mask_texture),
242                'head_model': ba.getmodel(char.head_model),
243                'torso_model': ba.getmodel(char.torso_model),
244                'pelvis_model': ba.getmodel(char.pelvis_model),
245                'upper_arm_model': ba.getmodel(char.upper_arm_model),
246                'forearm_model': ba.getmodel(char.forearm_model),
247                'hand_model': ba.getmodel(char.hand_model),
248                'upper_leg_model': ba.getmodel(char.upper_leg_model),
249                'lower_leg_model': ba.getmodel(char.lower_leg_model),
250                'toes_model': ba.getmodel(char.toes_model)
251            }
252        else:
253            media = self.spaz_media[character]
254        return media

Return the set of media used by this variant of spaz.

@classmethod
def get(cls) -> bastd.actor.spazfactory.SpazFactory:
256    @classmethod
257    def get(cls) -> SpazFactory:
258        """Return the shared ba.SpazFactory, creating it if necessary."""
259        # pylint: disable=cyclic-import
260        activity = ba.getactivity()
261        factory = activity.customdata.get(cls._STORENAME)
262        if factory is None:
263            factory = activity.customdata[cls._STORENAME] = SpazFactory()
264        assert isinstance(factory, SpazFactory)
265        return factory

Return the shared ba.SpazFactory, creating it if necessary.