- audio panel file browser can be used to assign file paths to sound effects
- sound effect assignments are automatically saved to config file - Vector getattr raises AttributeError like Python default class method
This commit is contained in:
parent
a30464edf5
commit
697eb22633
189
pgfw/Audio.py
189
pgfw/Audio.py
|
@ -1,4 +1,4 @@
|
|||
import os, re, pygame
|
||||
import os, re, shutil, pygame
|
||||
|
||||
from .GameChild import *
|
||||
from .Sprite import *
|
||||
|
@ -51,15 +51,15 @@ class Audio(GameChild):
|
|||
# - repository paths are not loaded at init but can replace loaded paths
|
||||
# and get written to config file
|
||||
#
|
||||
def load_sfx(self, path=None):
|
||||
def load_sfx(self, sfx_location=None):
|
||||
for name, path in self.get_configuration("sfx").items():
|
||||
self.load_sfx_file(sfx, path, name, True)
|
||||
if path is None:
|
||||
path = self.get_configuration("audio", "sfx-project-path") + \
|
||||
self.load_sfx_file(path, name, True)
|
||||
if sfx_location is None:
|
||||
sfx_location = self.get_configuration("audio", "sfx-project-path") + \
|
||||
self.get_configuration("audio", "sfx-default-path")
|
||||
if isinstance(path, str):
|
||||
path = [path]
|
||||
for root in path:
|
||||
if isinstance(sfx_location, str):
|
||||
sfx_location = [sfx_location]
|
||||
for root in sfx_location:
|
||||
prefix = ""
|
||||
root = self.get_resource(root)
|
||||
print("checking {} for sound effects".format(root))
|
||||
|
@ -80,11 +80,13 @@ class Audio(GameChild):
|
|||
if path and path.split(".")[-1] in self.get_configuration("audio", "sfx-extensions"):
|
||||
if name is None:
|
||||
name = prefix + re.sub("\.[^.]*$", "", os.path.basename(path))
|
||||
if replace and name in self.sfx:
|
||||
print("skipping existing sound effect for %s => %s" % (path, name))
|
||||
if not replace and name in self.sfx:
|
||||
print("skipping existing sound effect for %s: %s" % (name, path))
|
||||
else:
|
||||
print("loading sound effect %s into %s" % (path, name))
|
||||
self.sfx[name] = SoundEffect(self, path)
|
||||
return True
|
||||
return False
|
||||
|
||||
def update(self):
|
||||
for ii in range(pygame.mixer.get_num_channels()):
|
||||
|
@ -128,25 +130,26 @@ class AudioPanel(Animation):
|
|||
|
||||
def __init__(self, parent):
|
||||
Animation.__init__(self, parent)
|
||||
self.file_browser = AudioPanelFileBrowser(self)
|
||||
self.subscribe(self.respond)
|
||||
self.subscribe(self.respond, pygame.MOUSEBUTTONDOWN)
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.selected = None
|
||||
self.row_offset = 0
|
||||
self.deactivate()
|
||||
|
||||
def activate(self):
|
||||
pygame.mouse.set_visible(True)
|
||||
self.active = True
|
||||
self.set_rows()
|
||||
self.build()
|
||||
|
||||
def deactivate(self):
|
||||
pygame.mouse.set_visible(self.get_configuration("mouse", "visible"))
|
||||
self.active = False
|
||||
|
||||
# button 5 means fingers slide up
|
||||
# button 4 means finders slide down
|
||||
def respond(self, event):
|
||||
print("audio panel received %s" % event)
|
||||
if self.get_delegate().compare(event, "toggle-audio-panel") and \
|
||||
pygame.key.get_mods() & KMOD_CTRL and pygame.key.get_mods() & KMOD_SHIFT:
|
||||
if self.active:
|
||||
|
@ -154,14 +157,25 @@ class AudioPanel(Animation):
|
|||
else:
|
||||
self.activate()
|
||||
elif self.active:
|
||||
if event.type == pygame.MOUSEBUTTONDOWN:
|
||||
print("audio panel received mouse down event %s" % event)
|
||||
if event.button == 5:
|
||||
if event.type == pygame.MOUSEBUTTONDOWN and self.file_browser.is_hidden():
|
||||
if event.button == 1:
|
||||
for row in self.rows:
|
||||
if row.location.collidepoint(event.pos):
|
||||
if row != self.selected:
|
||||
self.file_browser.visit(self.file_browser.HOME)
|
||||
self.selected = row
|
||||
self.selected.start_blinking()
|
||||
self.file_browser.unhide()
|
||||
else:
|
||||
if row == self.selected:
|
||||
self.selected = None
|
||||
row.stop_blinking()
|
||||
elif event.button == 5:
|
||||
self.row_offset += 1
|
||||
elif event.button == 4:
|
||||
self.row_offset -= 1
|
||||
|
||||
def set_rows(self):
|
||||
def build(self):
|
||||
self.rows = []
|
||||
for key in sorted(self.parent.sfx):
|
||||
self.rows.append(AudioPanelRow(self, key))
|
||||
|
@ -179,21 +193,24 @@ class AudioPanel(Animation):
|
|||
row.update()
|
||||
corner.y += row.location.height + self.MARGIN
|
||||
index += 1
|
||||
self.file_browser.update()
|
||||
|
||||
|
||||
class AudioPanelRow(Sprite):
|
||||
class AudioPanelRow(BlinkingSprite):
|
||||
|
||||
BACKGROUND = pygame.Color(255, 255, 255)
|
||||
FOREGROUND = pygame.Color(0, 0, 0)
|
||||
BACKGROUND = pygame.Color(255, 255, 255, 180)
|
||||
FOREGROUND = pygame.Color(0, 0, 0, 180)
|
||||
WIDTH = .6
|
||||
FONT_SIZE = 18
|
||||
HEIGHT = 30
|
||||
INDENT = 4
|
||||
MAX_NAME_WIDTH = .7
|
||||
|
||||
def __init__(self, parent, key):
|
||||
Sprite.__init__(self, parent)
|
||||
BlinkingSprite.__init__(self, parent, 500)
|
||||
self.key = key
|
||||
self.build()
|
||||
self.halt()
|
||||
|
||||
def build(self):
|
||||
font = pygame.font.Font(
|
||||
|
@ -206,6 +223,10 @@ class AudioPanelRow(Sprite):
|
|||
self.add_frame(surface)
|
||||
name_sprite = Sprite(self)
|
||||
name = font.render(self.key + ":", True, self.FOREGROUND)
|
||||
if name.get_width() > int(self.location.w * self.MAX_NAME_WIDTH):
|
||||
crop = pygame.Rect(0, 0, int(self.location.w * self.MAX_NAME_WIDTH), name.get_height())
|
||||
crop.right = name.get_rect().right
|
||||
name = name.subsurface(crop)
|
||||
name_sprite.add_frame(name)
|
||||
name_sprite.display_surface = surface
|
||||
name_sprite.location.midleft = self.INDENT, self.location.centery
|
||||
|
@ -218,9 +239,6 @@ class AudioPanelRow(Sprite):
|
|||
file_sprite.add_frame(box)
|
||||
file_sprite.location.midright = self.location.right - self.INDENT, self.location.centery
|
||||
file_sprite.display_surface = surface
|
||||
# file_text = render_box(
|
||||
# font, self.get_sound_effect().path, color=self.FOREGROUND,
|
||||
# border=self.FOREGROUND, padding=2)
|
||||
file_name_sprite = Sprite(self)
|
||||
file_name_text = font.render(self.get_sound_effect().path, True, self.FOREGROUND)
|
||||
file_name_sprite.add_frame(file_name_text)
|
||||
|
@ -231,3 +249,124 @@ class AudioPanelRow(Sprite):
|
|||
|
||||
def get_sound_effect(self):
|
||||
return self.get_game().get_audio().sfx[self.key]
|
||||
|
||||
|
||||
class AudioPanelFileBrowser(Sprite):
|
||||
|
||||
WIDTH = .75
|
||||
HEIGHT = .75
|
||||
BACKGROUND = pygame.Color(255, 255, 255)
|
||||
FOREGROUND = pygame.Color(0, 0, 0)
|
||||
HOME, UP = "[HOME]", "[UP]"
|
||||
FONT_SIZE = 16
|
||||
|
||||
def __init__(self, parent):
|
||||
Sprite.__init__(self, parent)
|
||||
ds = self.get_display_surface()
|
||||
dsr = ds.get_rect()
|
||||
surface = pygame.Surface((dsr.w * self.WIDTH, dsr.h * self.HEIGHT), SRCALPHA)
|
||||
surface.fill(self.BACKGROUND)
|
||||
self.add_frame(surface)
|
||||
self.location.center = dsr.center
|
||||
self.reset()
|
||||
self.subscribe(self.respond, pygame.MOUSEBUTTONDOWN)
|
||||
|
||||
def reset(self):
|
||||
self.visit(self.HOME)
|
||||
self.hide()
|
||||
|
||||
def respond(self, event):
|
||||
if not self.is_hidden():
|
||||
if event.button == 1:
|
||||
if self.location.collidepoint(event.pos):
|
||||
for row in self.rows:
|
||||
if row.collide(Vector(*event.pos).get_moved(-self.location.left, -self.location.top)):
|
||||
full_path = os.path.join(os.path.sep.join(self.trail), row.path)
|
||||
if row.path == self.HOME or row.path == self.UP or \
|
||||
os.path.isdir(full_path) and os.access(full_path, os.R_OK):
|
||||
self.visit(row.path)
|
||||
elif os.path.isfile(full_path) and os.access(full_path, os.R_OK):
|
||||
key = self.parent.selected.key
|
||||
if self.get_audio().load_sfx_file(full_path, key, True):
|
||||
if not self.get_configuration().has_section("sfx"):
|
||||
self.get_configuration().add_section("sfx")
|
||||
self.get_configuration().set("sfx", key, full_path)
|
||||
config_path = self.get_configuration().locate_project_config_file()
|
||||
backup_path = config_path + ".backup"
|
||||
shutil.copyfile(config_path, backup_path)
|
||||
self.get_configuration().write(open(config_path, "w"))
|
||||
self.hide()
|
||||
self.get_delegate().cancel_propagation()
|
||||
self.parent.build()
|
||||
else:
|
||||
self.hide()
|
||||
self.get_delegate().cancel_propagation()
|
||||
elif event.button == 4:
|
||||
self.row_offset -= 1
|
||||
elif event.button == 5:
|
||||
self.row_offset += 1
|
||||
|
||||
def visit(self, path):
|
||||
if path == self.UP and len(self.trail) > 1:
|
||||
path = self.trail[-2]
|
||||
self.trail = self.trail[:-2]
|
||||
self.visit(path)
|
||||
elif path != self.UP:
|
||||
self.row_offset = 0
|
||||
if path == self.HOME:
|
||||
self.trail = []
|
||||
self.paths = ["/"]
|
||||
for option in "sfx-repository-path", "sfx-default-path", "sfx-project-path":
|
||||
for sfx_location in self.get_configuration("audio", option):
|
||||
if self.get_resource(sfx_location):
|
||||
self.paths.append(self.get_resource(sfx_location))
|
||||
else:
|
||||
self.paths = [self.HOME]
|
||||
self.trail.append(path)
|
||||
if len(self.trail) > 1:
|
||||
self.paths.append(self.UP)
|
||||
self.paths.extend(sorted(os.listdir(os.path.sep.join(self.trail))))
|
||||
self.build()
|
||||
|
||||
def build(self):
|
||||
self.rows = []
|
||||
for path in self.paths:
|
||||
row = Sprite(self)
|
||||
font = pygame.font.Font(
|
||||
self.get_resource(self.get_configuration("audio", "panel-font")),
|
||||
self.FONT_SIZE)
|
||||
row.path = path
|
||||
text = font.render(path, True, self.FOREGROUND)
|
||||
surface = pygame.Surface((self.location.w, text.get_height()), SRCALPHA)
|
||||
surface.blit(text, (0, 0))
|
||||
row.add_frame(surface)
|
||||
row.display_surface = self.get_current_frame()
|
||||
row.location.bottom = 0
|
||||
self.rows.append(row)
|
||||
|
||||
def update(self):
|
||||
self.get_current_frame().fill(self.BACKGROUND)
|
||||
if not self.is_hidden():
|
||||
# ds = self.get_display_surface()
|
||||
# dsr = ds.get_rect()
|
||||
# corner = Vector(self.MARGIN, self.MARGIN)
|
||||
corner = Vector(0, 0)
|
||||
index = self.row_offset
|
||||
# while corner.y < dsr.height - self.MARGIN:
|
||||
for row in self.rows:
|
||||
row.remove_locations()
|
||||
row.location.bottom = 0
|
||||
# row.hide()
|
||||
while corner.y < self.location.h:
|
||||
row = self.rows[index % len(self.rows)]
|
||||
# row.unhide()
|
||||
if index - self.row_offset >= len(self.rows):
|
||||
row.add_location(corner.copy())
|
||||
else:
|
||||
row.location.topleft = corner.copy()
|
||||
# corner.y += row.location.height + self.MARGIN
|
||||
corner.y += row.location.height
|
||||
index += 1
|
||||
for row in self.rows:
|
||||
row.update()
|
||||
Sprite.update(self)
|
||||
|
|
|
@ -125,17 +125,21 @@ class Sprite(Animation):
|
|||
|
||||
def collide(self, other):
|
||||
if not isinstance(other, Rect):
|
||||
other = other.rect
|
||||
if hasattr(other, "rect"):
|
||||
other = other.rect
|
||||
else:
|
||||
other = Rect(other[0], other[1], 1, 1)
|
||||
for location in self.locations:
|
||||
if location.colliderect(other):
|
||||
return location
|
||||
|
||||
def collide_mask(self, other):
|
||||
if self.collide(other):
|
||||
location = self.collide(other)
|
||||
if location:
|
||||
current_mask = mask.from_surface(self.get_current_frame())
|
||||
other_mask = mask.from_surface(other.get_current_frame())
|
||||
offset = other.location.left - self.location.left, \
|
||||
other.location.top - self.location.top
|
||||
offset = other.location.left - location.left, \
|
||||
other.location.top - location.top
|
||||
return current_mask.overlap(other_mask, offset) is not None
|
||||
|
||||
def mirror(self):
|
||||
|
@ -479,6 +483,13 @@ class BlinkingSprite(Sprite):
|
|||
def blink(self):
|
||||
self.toggle_hidden()
|
||||
|
||||
def start_blinking(self):
|
||||
self.play(self.blink)
|
||||
|
||||
def stop_blinking(self):
|
||||
self.halt(self.blink)
|
||||
self.unhide()
|
||||
|
||||
|
||||
class RainbowSprite(Sprite):
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ class Vector(list):
|
|||
return self[0]
|
||||
elif name == "y":
|
||||
return self[1]
|
||||
else:
|
||||
raise AttributeError
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name == "x":
|
||||
|
@ -107,6 +109,7 @@ class EVector(Vector):
|
|||
|
||||
def __init__(self, x=0, y=0, dx=0, dy=0, magnitude=None, angle=0):
|
||||
Vector.__init__(self, x, y)
|
||||
self.angle = None
|
||||
self.set_step(dx, dy, magnitude, angle)
|
||||
|
||||
def set_step(self, dx=0, dy=0, magnitude=None, angle=0):
|
||||
|
|
Loading…
Reference in New Issue