vh-bombsquad-modded-server-.../dist/ba_data/python/bastd/actor/spazfactory.py
2024-02-19 18:29:19 +05:30

307 lines
11 KiB
Python

# Released under the MIT License. See LICENSE for details.
#
"""Provides a factory object from creating Spazzes."""
from __future__ import annotations
from typing import TYPE_CHECKING
import ba
import ba.internal
from bastd.gameutils import SharedObjects
if TYPE_CHECKING:
from typing import Any, Sequence
class SpazFactory:
"""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.
"""
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_weak: ba.Sound
"""A weak punch ba.Sound."""
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."""
_STORENAME = ba.storagename()
def _preload(self, character: str) -> None:
"""Preload media needed for a given character."""
self.get_media(character)
def __init__(self) -> None:
"""Instantiate a factory object."""
# pylint: disable=cyclic-import
# FIXME: should probably put these somewhere common so we don't
# have to import them from a module that imports us.
from bastd.actor.spaz import (
PickupMessage,
PunchHitMessage,
CurseExplodeMessage,
)
shared = SharedObjects.get()
self.impact_sounds_medium = (
ba.getsound('impactMedium'),
ba.getsound('impactMedium2'),
)
self.impact_sounds_hard = (
ba.getsound('impactHard'),
ba.getsound('impactHard2'),
ba.getsound('impactHard3'),
)
self.impact_sounds_harder = (
ba.getsound('bigImpact'),
ba.getsound('bigImpact2'),
)
self.single_player_death_sound = ba.getsound('playerDeath')
self.punch_sound_weak = ba.getsound('punchWeak01')
self.punch_sound = ba.getsound('punch01')
self.punch_sound_strong = (
ba.getsound('punchStrong01'),
ba.getsound('punchStrong02'),
)
self.punch_sound_stronger = ba.getsound('superPunch')
self.swish_sound = ba.getsound('punchSwish')
self.block_sound = ba.getsound('block')
self.shatter_sound = ba.getsound('shatter')
self.splatter_sound = ba.getsound('splatter')
self.spaz_material = ba.Material()
self.roller_material = ba.Material()
self.punch_material = ba.Material()
self.pickup_material = ba.Material()
self.curse_material = ba.Material()
footing_material = shared.footing_material
object_material = shared.object_material
player_material = shared.player_material
region_material = shared.region_material
# Send footing messages to spazzes so they know when they're on
# solid ground.
# Eww; this probably should just be built into the spaz node.
self.roller_material.add_actions(
conditions=('they_have_material', footing_material),
actions=(
('message', 'our_node', 'at_connect', 'footing', 1),
('message', 'our_node', 'at_disconnect', 'footing', -1),
),
)
self.spaz_material.add_actions(
conditions=('they_have_material', footing_material),
actions=(
('message', 'our_node', 'at_connect', 'footing', 1),
('message', 'our_node', 'at_disconnect', 'footing', -1),
),
)
# Punches.
self.punch_material.add_actions(
conditions=('they_are_different_node_than_us',),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect', PunchHitMessage()),
),
)
# Pickups.
self.pickup_material.add_actions(
conditions=(
('they_are_different_node_than_us',),
'and',
('they_have_material', object_material),
),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', False),
('message', 'our_node', 'at_connect', PickupMessage()),
),
)
# Curse.
self.curse_material.add_actions(
conditions=(
('they_are_different_node_than_us',),
'and',
('they_have_material', player_material),
),
actions=(
'message',
'our_node',
'at_connect',
CurseExplodeMessage(),
),
)
self.foot_impact_sounds = (
ba.getsound('footImpact01'),
ba.getsound('footImpact02'),
ba.getsound('footImpact03'),
)
self.foot_skid_sound = ba.getsound('skid01')
self.foot_roll_sound = ba.getsound('scamper01')
self.roller_material.add_actions(
conditions=('they_have_material', footing_material),
actions=(
('impact_sound', self.foot_impact_sounds, 1, 0.2),
('skid_sound', self.foot_skid_sound, 20, 0.3),
('roll_sound', self.foot_roll_sound, 20, 3.0),
),
)
self.skid_sound = ba.getsound('gravelSkid')
self.spaz_material.add_actions(
conditions=('they_have_material', footing_material),
actions=(
('impact_sound', self.foot_impact_sounds, 20, 6),
('skid_sound', self.skid_sound, 2.0, 1),
('roll_sound', self.skid_sound, 2.0, 1),
),
)
self.shield_up_sound = ba.getsound('shieldUp')
self.shield_down_sound = ba.getsound('shieldDown')
self.shield_hit_sound = ba.getsound('shieldHit')
# We don't want to collide with stuff we're initially overlapping
# (unless its marked with a special region material).
self.spaz_material.add_actions(
conditions=(
(
('we_are_younger_than', 51),
'and',
('they_are_different_node_than_us',),
),
'and',
('they_dont_have_material', region_material),
),
actions=('modify_node_collision', 'collide', False),
)
self.spaz_media: dict[str, Any] = {}
# Lets load some basic rules.
# (allows them to be tweaked from the master server)
self.shield_decay_rate = ba.internal.get_v1_account_misc_read_val(
'rsdr', 10.0
)
self.punch_cooldown = ba.internal.get_v1_account_misc_read_val(
'rpc', 400
)
self.punch_cooldown_gloves = ba.internal.get_v1_account_misc_read_val(
'rpcg', 300
)
self.punch_power_scale = ba.internal.get_v1_account_misc_read_val(
'rpp', 1.2
)
self.punch_power_scale_gloves = (
ba.internal.get_v1_account_misc_read_val('rppg', 1.4)
)
self.max_shield_spillover_damage = (
ba.internal.get_v1_account_misc_read_val('rsms', 500)
)
def get_style(self, character: str) -> str:
"""Return the named style for this character.
(this influences subtle aspects of their appearance, etc)
"""
return ba.app.spaz_appearances[character].style
def get_media(self, character: str) -> dict[str, Any]:
"""Return the set of media used by this variant of spaz."""
char = ba.app.spaz_appearances[character]
if character not in self.spaz_media:
media = self.spaz_media[character] = {
'jump_sounds': [ba.getsound(s) for s in char.jump_sounds],
'attack_sounds': [ba.getsound(s) for s in char.attack_sounds],
'impact_sounds': [ba.getsound(s) for s in char.impact_sounds],
'death_sounds': [ba.getsound(s) for s in char.death_sounds],
'pickup_sounds': [ba.getsound(s) for s in char.pickup_sounds],
'fall_sounds': [ba.getsound(s) for s in char.fall_sounds],
'color_texture': ba.gettexture(char.color_texture),
'color_mask_texture': ba.gettexture(char.color_mask_texture),
'head_model': ba.getmodel(char.head_model),
'torso_model': ba.getmodel(char.torso_model),
'pelvis_model': ba.getmodel(char.pelvis_model),
'upper_arm_model': ba.getmodel(char.upper_arm_model),
'forearm_model': ba.getmodel(char.forearm_model),
'hand_model': ba.getmodel(char.hand_model),
'upper_leg_model': ba.getmodel(char.upper_leg_model),
'lower_leg_model': ba.getmodel(char.lower_leg_model),
'toes_model': ba.getmodel(char.toes_model),
}
else:
media = self.spaz_media[character]
return media
@classmethod
def get(cls) -> SpazFactory:
"""Return the shared ba.SpazFactory, creating it if necessary."""
# pylint: disable=cyclic-import
activity = ba.getactivity()
factory = activity.customdata.get(cls._STORENAME)
if factory is None:
factory = activity.customdata[cls._STORENAME] = SpazFactory()
assert isinstance(factory, SpazFactory)
return factory