Cleaning up factory classes

This commit is contained in:
Eric Froemling 2020-06-18 12:41:30 -07:00
parent c5b0d977e3
commit bc22359dcd
8 changed files with 68 additions and 64 deletions

View File

@ -34,10 +34,6 @@ from bastd.gameutils import SharedObjects
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Sequence, Optional, Callable, List, Tuple, Type from typing import Any, Sequence, Optional, Callable, List, Tuple, Type
# Attr we store these objects as on the current activity.
# (based on our module so hopefully avoids conflicts)
STORAGE_ATTR_NAME = '_' + __name__.replace('.', '_') + '_bombfactory'
PlayerType = TypeVar('PlayerType', bound='ba.Player') PlayerType = TypeVar('PlayerType', bound='ba.Player')
@ -150,14 +146,16 @@ class BombFactory:
ba.Sound for a rolling bomb. ba.Sound for a rolling bomb.
""" """
@staticmethod _STORENAME = ba.storagename()
def get() -> BombFactory:
@classmethod
def get(cls) -> BombFactory:
"""Get/create a shared bastd.actor.bomb.BombFactory object.""" """Get/create a shared bastd.actor.bomb.BombFactory object."""
activity = ba.getactivity() activity = ba.getactivity()
factory = getattr(activity, STORAGE_ATTR_NAME, None) factory = activity.customdata.get(cls._STORENAME)
if factory is None: if factory is None:
factory = BombFactory() factory = BombFactory()
setattr(activity, STORAGE_ATTR_NAME, factory) activity.customdata[cls._STORENAME] = factory
assert isinstance(factory, BombFactory) assert isinstance(factory, BombFactory)
return factory return factory

View File

@ -59,6 +59,8 @@ class FlagFactory:
The ba.Texture for flags. The ba.Texture for flags.
""" """
_STORENAME = ba.storagename()
def __init__(self) -> None: def __init__(self) -> None:
"""Instantiate a FlagFactory. """Instantiate a FlagFactory.
@ -123,14 +125,14 @@ class FlagFactory:
self.flag_texture = ba.gettexture('flagColor') self.flag_texture = ba.gettexture('flagColor')
@staticmethod @classmethod
def get() -> FlagFactory: def get(cls) -> FlagFactory:
"""Get/create a shared FlagFactory instance.""" """Get/create a shared FlagFactory instance."""
activity = ba.getactivity() activity = ba.getactivity()
factory = getattr(activity, 'shared_flag_factory', None) factory = activity.customdata.get(cls._STORENAME)
if factory is None: if factory is None:
factory = FlagFactory() factory = FlagFactory()
setattr(activity, 'shared_flag_factory', factory) activity.customdata[cls._STORENAME] = factory
assert isinstance(factory, FlagFactory) assert isinstance(factory, FlagFactory)
return factory return factory

View File

@ -29,6 +29,7 @@ from typing import TYPE_CHECKING
import ba import ba
from bastd.actor import bomb as stdbomb from bastd.actor import bomb as stdbomb
from bastd.actor.powerupbox import PowerupBoxFactory from bastd.actor.powerupbox import PowerupBoxFactory
from bastd.actor.spazfactory import SpazFactory
from bastd.gameutils import SharedObjects from bastd.gameutils import SharedObjects
if TYPE_CHECKING: if TYPE_CHECKING:
@ -56,18 +57,6 @@ class BombDiedMessage:
"""A bomb has died and thus can be recycled.""" """A bomb has died and thus can be recycled."""
def get_factory() -> SpazFactory:
"""Return the shared ba.SpazFactory object, creating it if necessary."""
# pylint: disable=cyclic-import
from bastd.actor.spazfactory import SpazFactory
activity = ba.getactivity()
factory = getattr(activity, 'shared_spaz_factory', None)
if factory is None:
factory = activity.shared_spaz_factory = SpazFactory() # type: ignore
assert isinstance(factory, SpazFactory)
return factory
class Spaz(ba.Actor): class Spaz(ba.Actor):
""" """
Base class for various Spazzes. Base class for various Spazzes.
@ -112,7 +101,7 @@ class Spaz(ba.Actor):
shared = SharedObjects.get() shared = SharedObjects.get()
activity = self.activity activity = self.activity
factory = get_factory() factory = SpazFactory.get()
# we need to behave slightly different in the tutorial # we need to behave slightly different in the tutorial
self._demo_mode = demo_mode self._demo_mode = demo_mode
@ -466,7 +455,7 @@ class Spaz(ba.Actor):
ba.timer( ba.timer(
0.1, 0.1,
ba.WeakCall(self._safe_play_sound, ba.WeakCall(self._safe_play_sound,
get_factory().swish_sound, 0.8)) SpazFactory.get().swish_sound, 0.8))
self._turbo_filter_add_press('punch') self._turbo_filter_add_press('punch')
def _safe_play_sound(self, sound: ba.Sound, volume: float) -> None: def _safe_play_sound(self, sound: ba.Sound, volume: float) -> None:
@ -605,7 +594,7 @@ class Spaz(ba.Actor):
he will explode in 5 seconds. he will explode in 5 seconds.
""" """
if not self._cursed: if not self._cursed:
factory = get_factory() factory = SpazFactory.get()
self._cursed = True self._cursed = True
# Add the curse material. # Add the curse material.
@ -637,7 +626,7 @@ class Spaz(ba.Actor):
self._punch_power_scale = 1.7 self._punch_power_scale = 1.7
self._punch_cooldown = 300 self._punch_cooldown = 300
else: else:
factory = get_factory() factory = SpazFactory.get()
self._punch_power_scale = factory.punch_power_scale_gloves self._punch_power_scale = factory.punch_power_scale_gloves
self._punch_cooldown = factory.punch_cooldown_gloves self._punch_cooldown = factory.punch_cooldown_gloves
@ -650,7 +639,7 @@ class Spaz(ba.Actor):
ba.print_error('Can\'t equip shields; no node.') ba.print_error('Can\'t equip shields; no node.')
return return
factory = get_factory() factory = SpazFactory.get()
if self.shield is None: if self.shield is None:
self.shield = ba.newnode('shield', self.shield = ba.newnode('shield',
owner=self.node, owner=self.node,
@ -685,7 +674,7 @@ class Spaz(ba.Actor):
self.shield = None self.shield = None
self.shield_decay_timer = None self.shield_decay_timer = None
assert self.node assert self.node
ba.playsound(get_factory().shield_down_sound, ba.playsound(SpazFactory.get().shield_down_sound,
1.0, 1.0,
position=self.node.position) position=self.node.position)
else: else:
@ -802,7 +791,7 @@ class Spaz(ba.Actor):
ba.WeakCall(self._gloves_wear_off), ba.WeakCall(self._gloves_wear_off),
timeformat=ba.TimeFormat.MILLISECONDS)) timeformat=ba.TimeFormat.MILLISECONDS))
elif msg.poweruptype == 'shield': elif msg.poweruptype == 'shield':
factory = get_factory() factory = SpazFactory.get()
# Let's allow powerup-equipped shields to lose hp over time. # Let's allow powerup-equipped shields to lose hp over time.
self.equip_shields(decay=factory.shield_decay_rate > 0) self.equip_shields(decay=factory.shield_decay_rate > 0)
@ -832,7 +821,7 @@ class Spaz(ba.Actor):
self._cursed = False self._cursed = False
# Remove cursed material. # Remove cursed material.
factory = get_factory() factory = SpazFactory.get()
for attr in ['materials', 'roller_materials']: for attr in ['materials', 'roller_materials']:
materials = getattr(self.node, attr) materials = getattr(self.node, attr)
if factory.curse_material in materials: if factory.curse_material in materials:
@ -856,7 +845,7 @@ class Spaz(ba.Actor):
if not self.node: if not self.node:
return None return None
if self.node.invincible: if self.node.invincible:
ba.playsound(get_factory().block_sound, ba.playsound(SpazFactory.get().block_sound,
1.0, 1.0,
position=self.node.position) position=self.node.position)
return None return None
@ -881,7 +870,7 @@ class Spaz(ba.Actor):
if not self.node: if not self.node:
return None return None
if self.node.invincible: if self.node.invincible:
ba.playsound(get_factory().block_sound, ba.playsound(SpazFactory.get().block_sound,
1.0, 1.0,
position=self.node.position) position=self.node.position)
return True return True
@ -924,13 +913,13 @@ class Spaz(ba.Actor):
# without damaging the player. # without damaging the player.
# However, massive damage events should still be able to # However, massive damage events should still be able to
# damage the player. This hopefully gives us a happy medium. # damage the player. This hopefully gives us a happy medium.
max_spillover = get_factory().max_shield_spillover_damage max_spillover = SpazFactory.get().max_shield_spillover_damage
if self.shield_hitpoints <= 0: if self.shield_hitpoints <= 0:
# FIXME: Transition out perhaps? # FIXME: Transition out perhaps?
self.shield.delete() self.shield.delete()
self.shield = None self.shield = None
ba.playsound(get_factory().shield_down_sound, ba.playsound(SpazFactory.get().shield_down_sound,
1.0, 1.0,
position=self.node.position) position=self.node.position)
@ -944,7 +933,7 @@ class Spaz(ba.Actor):
chunk_type='spark') chunk_type='spark')
else: else:
ba.playsound(get_factory().shield_hit_sound, ba.playsound(SpazFactory.get().shield_hit_sound,
0.5, 0.5,
position=self.node.position) position=self.node.position)
@ -1001,14 +990,14 @@ class Spaz(ba.Actor):
# Let's always add in a super-punch sound with boxing # Let's always add in a super-punch sound with boxing
# gloves just to differentiate them. # gloves just to differentiate them.
if msg.hit_subtype == 'super_punch': if msg.hit_subtype == 'super_punch':
ba.playsound(get_factory().punch_sound_stronger, ba.playsound(SpazFactory.get().punch_sound_stronger,
1.0, 1.0,
position=self.node.position) position=self.node.position)
if damage > 500: if damage > 500:
sounds = get_factory().punch_sound_strong sounds = SpazFactory.get().punch_sound_strong
sound = sounds[random.randrange(len(sounds))] sound = sounds[random.randrange(len(sounds))]
else: else:
sound = get_factory().punch_sound sound = SpazFactory.get().punch_sound
ba.playsound(sound, 1.0, position=self.node.position) ba.playsound(sound, 1.0, position=self.node.position)
# Throw up some chunks. # Throw up some chunks.
@ -1118,7 +1107,7 @@ class Spaz(ba.Actor):
elif self.node: elif self.node:
self.node.hurt = 1.0 self.node.hurt = 1.0
if self.play_big_death_sound and not wasdead: if self.play_big_death_sound and not wasdead:
ba.playsound(get_factory().single_player_death_sound) ba.playsound(SpazFactory.get().single_player_death_sound)
self.node.dead = True self.node.dead = True
ba.timer(2.0, self.node.delete) ba.timer(2.0, self.node.delete)
@ -1160,7 +1149,7 @@ class Spaz(ba.Actor):
# If its something besides another spaz, just do a muffled # If its something besides another spaz, just do a muffled
# punch sound. # punch sound.
if node.getnodetype() != 'spaz': if node.getnodetype() != 'spaz':
sounds = get_factory().impact_sounds_medium sounds = SpazFactory.get().impact_sounds_medium
sound = sounds[random.randrange(len(sounds))] sound = sounds[random.randrange(len(sounds))]
ba.playsound(sound, 1.0, position=self.node.position) ba.playsound(sound, 1.0, position=self.node.position)
@ -1347,11 +1336,11 @@ class Spaz(ba.Actor):
scale=0.3, scale=0.3,
spread=0.2, spread=0.2,
chunk_type='ice') chunk_type='ice')
ba.playsound(get_factory().shatter_sound, ba.playsound(SpazFactory.get().shatter_sound,
1.0, 1.0,
position=self.node.position) position=self.node.position)
else: else:
ba.playsound(get_factory().splatter_sound, ba.playsound(SpazFactory.get().splatter_sound,
1.0, 1.0,
position=self.node.position) position=self.node.position)
self.handlemessage(ba.DieMessage()) self.handlemessage(ba.DieMessage())
@ -1369,11 +1358,11 @@ class Spaz(ba.Actor):
self.node.handlemessage('knockout', max(0.0, 50.0 * intensity)) self.node.handlemessage('knockout', max(0.0, 50.0 * intensity))
sounds: Sequence[ba.Sound] sounds: Sequence[ba.Sound]
if intensity > 5.0: if intensity > 5.0:
sounds = get_factory().impact_sounds_harder sounds = SpazFactory.get().impact_sounds_harder
elif intensity > 3.0: elif intensity > 3.0:
sounds = get_factory().impact_sounds_hard sounds = SpazFactory.get().impact_sounds_hard
else: else:
sounds = get_factory().impact_sounds_medium sounds = SpazFactory.get().impact_sounds_medium
sound = sounds[random.randrange(len(sounds))] sound = sounds[random.randrange(len(sounds))]
ba.playsound(sound, position=pos, volume=5.0) ba.playsound(sound, position=pos, volume=5.0)
@ -1418,7 +1407,7 @@ class Spaz(ba.Actor):
self._punch_power_scale = 1.2 self._punch_power_scale = 1.2
self._punch_cooldown = BASE_PUNCH_COOLDOWN self._punch_cooldown = BASE_PUNCH_COOLDOWN
else: else:
factory = get_factory() factory = SpazFactory.get()
self._punch_power_scale = factory.punch_power_scale self._punch_power_scale = factory.punch_power_scale
self._punch_cooldown = factory.punch_cooldown self._punch_cooldown = factory.punch_cooldown
self._has_boxing_gloves = False self._has_boxing_gloves = False

View File

@ -26,8 +26,6 @@ from typing import TYPE_CHECKING
import ba import ba
from bastd.gameutils import SharedObjects from bastd.gameutils import SharedObjects
from bastd.actor.spaz import (PickupMessage, PunchHitMessage,
CurseExplodeMessage)
import _ba import _ba
if TYPE_CHECKING: if TYPE_CHECKING:
@ -97,12 +95,20 @@ class SpazFactory:
A ba.Material applied to a cursed ba.Spaz that triggers an explosion. A ba.Material applied to a cursed ba.Spaz that triggers an explosion.
""" """
_STORENAME = ba.storagename()
def _preload(self, character: str) -> None: def _preload(self, character: str) -> None:
"""Preload media needed for a given character.""" """Preload media needed for a given character."""
self.get_media(character) self.get_media(character)
def __init__(self) -> None: def __init__(self) -> None:
"""Instantiate a factory object.""" """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() shared = SharedObjects.get()
self.impact_sounds_medium = (ba.getsound('impactMedium'), self.impact_sounds_medium = (ba.getsound('impactMedium'),
ba.getsound('impactMedium2')) ba.getsound('impactMedium2'))
@ -265,3 +271,14 @@ class SpazFactory:
else: else:
media = self.spaz_media[character] media = self.spaz_media[character]
return media 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

View File

@ -28,7 +28,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import ba import ba
from bastd.actor.spaz import get_factory from bastd.actor.spazfactory import SpazFactory
from bastd.actor.scoreboard import Scoreboard from bastd.actor.scoreboard import Scoreboard
if TYPE_CHECKING: if TYPE_CHECKING:
@ -525,7 +525,7 @@ class EliminationGame(ba.TeamGameActivity[Player, Team]):
# Play big death sound on our last death # Play big death sound on our last death
# or for every one in solo mode. # or for every one in solo mode.
if self._solo_mode or player.lives == 0: if self._solo_mode or player.lives == 0:
ba.playsound(get_factory().single_player_death_sound) ba.playsound(SpazFactory.get().single_player_death_sound)
# If we hit zero lives, we're dead (and our team might be too). # If we hit zero lives, we're dead (and our team might be too).
if player.lives == 0: if player.lives == 0:

View File

@ -29,10 +29,6 @@ import ba
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Sequence, Optional from typing import Sequence, Optional
# Attr we store these objects as on the current activity.
# (based on our module so hopefully avoids conflicts)
STORAGE_ATTR_NAME = '_' + __name__.replace('.', '_') + '_sharedobjs'
class SharedObjects: class SharedObjects:
"""Various common components for use in games. """Various common components for use in games.
@ -44,9 +40,11 @@ class SharedObjects:
standard materials. standard materials.
""" """
_STORENAME = ba.storagename()
def __init__(self) -> None: def __init__(self) -> None:
activity = ba.getactivity() activity = ba.getactivity()
if hasattr(activity, STORAGE_ATTR_NAME): if hasattr(activity, self._STORENAME):
raise RuntimeError('Use SharedObjects.get() to fetch the' raise RuntimeError('Use SharedObjects.get() to fetch the'
' shared instance for this activity.') ' shared instance for this activity.')
self._object_material: Optional[ba.Material] = None self._object_material: Optional[ba.Material] = None
@ -58,14 +56,14 @@ class SharedObjects:
self._region_material: Optional[ba.Material] = None self._region_material: Optional[ba.Material] = None
self._railing_material: Optional[ba.Material] = None self._railing_material: Optional[ba.Material] = None
@staticmethod @classmethod
def get() -> SharedObjects: def get(cls) -> SharedObjects:
"""Fetch/create the instance of this class for the current activity.""" """Fetch/create the instance of this class for the current activity."""
activity = ba.getactivity() activity = ba.getactivity()
shobs = getattr(activity, STORAGE_ATTR_NAME, None) shobs = activity.customdata.get(cls._STORENAME)
if shobs is None: if shobs is None:
shobs = SharedObjects() shobs = SharedObjects()
setattr(activity, STORAGE_ATTR_NAME, shobs) activity.customdata[cls._STORENAME] = shobs
assert isinstance(shobs, SharedObjects) assert isinstance(shobs, SharedObjects)
return shobs return shobs

View File

@ -28,7 +28,6 @@ import weakref
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import ba import ba
from bastd.actor import spaz
import _ba import _ba
if TYPE_CHECKING: if TYPE_CHECKING:
@ -886,6 +885,7 @@ def _preload2() -> None:
def _preload3() -> None: def _preload3() -> None:
from bastd.actor.spazfactory import SpazFactory
for mname in ['bomb', 'bombSticky', 'impactBomb']: for mname in ['bomb', 'bombSticky', 'impactBomb']:
ba.getmodel(mname) ba.getmodel(mname)
for tname in [ for tname in [
@ -895,7 +895,7 @@ def _preload3() -> None:
ba.gettexture(tname) ba.gettexture(tname)
for sname in ['freeze', 'fuse01', 'activateBeep', 'warnBeep']: for sname in ['freeze', 'fuse01', 'activateBeep', 'warnBeep']:
ba.getsound(sname) ba.getsound(sname)
spaz.get_factory() SpazFactory.get()
ba.timer(0.2, _preload4) ba.timer(0.2, _preload4)

View File

@ -120,7 +120,7 @@ def get_target(path: str) -> None:
# Just expand it and it get placed wherever it belongs. # Just expand it and it get placed wherever it belongs.
# Strangely, decompressing lots of these simultaneously leads to occasional # Strangely, decompressing lots of these simultaneously leads to occasional
# "File does not exist" errors when running on Windows Subystem for Linux. # "File does not exist" errors when running on Windows Subsystem for Linux.
# There should be no overlap in files getting written, but perhaps # There should be no overlap in files getting written, but perhaps
# something about how tar rebuilds the directory structure causes clashes. # something about how tar rebuilds the directory structure causes clashes.
# It seems that just explicitly creating necessary directories first # It seems that just explicitly creating necessary directories first