- confirm quit
- bgm added to audio panel - delegate maintains order subscribers are added by object - optionally suppress "any key" event when mods are pressed - dict of named child objects added to sprites - generate glow animation effect frames
This commit is contained in:
parent
8ce09138d2
commit
cf8021c480
|
@ -38,6 +38,8 @@ class Animation(GameChild):
|
|||
def get_default(self, method=None):
|
||||
if not method:
|
||||
method = self.default_method
|
||||
elif isinstance(method, str):
|
||||
return getattr(self, method)
|
||||
return method
|
||||
|
||||
def halt(self, method=None):
|
||||
|
|
252
pgfw/Audio.py
252
pgfw/Audio.py
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os, re, shutil, pygame
|
||||
import os, re, shutil, pygame, sys
|
||||
|
||||
from .GameChild import *
|
||||
from .Sprite import *
|
||||
|
@ -23,6 +23,8 @@ class Audio(GameChild):
|
|||
self.subscribe(self.respond)
|
||||
self.sfx = {}
|
||||
self.load_sfx()
|
||||
self.bgm = {}
|
||||
self.load_bgm()
|
||||
|
||||
def set_volume(self, volume=None, increment=None, mute=False):
|
||||
if mute:
|
||||
|
@ -94,7 +96,7 @@ class Audio(GameChild):
|
|||
def load_sfx_file(self, path, name=None, replace=False, prefix="",
|
||||
volume=1.0, fade_out=0, loops=0, maxtime=0):
|
||||
path = self.get_resource(path)
|
||||
if path and path.split(".")[-1] in self.get_configuration("audio", "sfx-extensions"):
|
||||
if path and self.is_sound_file(path):
|
||||
if name is None:
|
||||
name = prefix + re.sub("\.[^.]*$", "", os.path.basename(path))
|
||||
if not replace and name in self.sfx:
|
||||
|
@ -106,6 +108,57 @@ class Audio(GameChild):
|
|||
return True
|
||||
return False
|
||||
|
||||
#
|
||||
# Loading BGM procedure
|
||||
#
|
||||
# - load config file name/path definitions
|
||||
# - check project specific bgm paths, load any that don't conflict
|
||||
# - other paths can replace loaded paths through the audio panel and get
|
||||
# written to config file
|
||||
#
|
||||
def load_bgm(self):
|
||||
for name, path in self.get_configuration("bgm").items():
|
||||
self.set_bgm(path, name)
|
||||
for root in self.get_configuration("audio", "bgm-project-path"):
|
||||
if os.path.exists(root):
|
||||
print("checking {} for music".format(root))
|
||||
if os.path.isfile(root):
|
||||
self.set_bgm(root)
|
||||
else:
|
||||
for node, branches, leaves in os.walk(root, followlinks=True):
|
||||
for leaf in leaves:
|
||||
prefix = re.sub(root, "", node)
|
||||
prefix = re.sub("^/", "", prefix)
|
||||
if prefix:
|
||||
prefix = re.sub("/", "_", prefix) + "_"
|
||||
self.set_bgm(os.path.join(node, leaf), prefix=prefix)
|
||||
|
||||
def set_bgm(self, path, name=None, prefix=""):
|
||||
print("setting {} music to {}".format(name, path))
|
||||
try:
|
||||
pygame.mixer.music.load(path)
|
||||
except:
|
||||
print("can't load {} as music".format(path))
|
||||
return False
|
||||
if name is None:
|
||||
name = os.path.basename(path).split(".")[0]
|
||||
self.bgm[prefix + name] = path
|
||||
return True
|
||||
|
||||
def play_bgm(self, name):
|
||||
pygame.mixer.music.load(self.bgm[name])
|
||||
pygame.mixer.music.play(-1)
|
||||
|
||||
def is_sound_file(self, path):
|
||||
return path.split(".")[-1] in self.get_configuration("audio", "sfx-extensions")
|
||||
|
||||
def is_loadable(self, path):
|
||||
try:
|
||||
pygame.mixer.Sound(path)
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
|
||||
def update(self):
|
||||
# for ii in range(pygame.mixer.get_num_channels()):
|
||||
# channel = pygame.mixer.Channel(ii)
|
||||
|
@ -231,9 +284,7 @@ class AudioPanel(Animation):
|
|||
self.active = False
|
||||
|
||||
def respond(self, event):
|
||||
if self.get_delegate().compare(event, "toggle-audio-panel") and \
|
||||
pygame.key.get_mods() & KMOD_CTRL and pygame.key.get_mods() & KMOD_SHIFT and \
|
||||
self.get_audio().sfx:
|
||||
if self.get_delegate().compare(event, "toggle-audio-panel") and self.get_audio().sfx:
|
||||
if self.active:
|
||||
self.deactivate()
|
||||
else:
|
||||
|
@ -246,12 +297,16 @@ class AudioPanel(Animation):
|
|||
self.row_offset += 1
|
||||
elif event.button == 4:
|
||||
self.row_offset -= 1
|
||||
elif event.button == 3:
|
||||
self.deactivate()
|
||||
|
||||
def build(self):
|
||||
for row in self.rows:
|
||||
row.unsubscribe()
|
||||
del row
|
||||
self.rows = []
|
||||
for key in sorted(self.parent.bgm):
|
||||
self.rows.append(AudioPanelRow(self, key, True))
|
||||
for key in sorted(self.parent.sfx):
|
||||
self.rows.append(AudioPanelRow(self, key))
|
||||
|
||||
|
@ -285,42 +340,52 @@ class AudioPanelRow(BlinkingSprite):
|
|||
SLIDER_W = 60
|
||||
BUTTON_W = 30
|
||||
|
||||
def __init__(self, parent, key):
|
||||
def __init__(self, parent, key, is_bgm=False):
|
||||
BlinkingSprite.__init__(self, parent, 500)
|
||||
self.key = key
|
||||
self.selected = False
|
||||
self.font = self.parent.font_large
|
||||
self.is_bgm = is_bgm
|
||||
self.build()
|
||||
font_medium = self.parent.font_medium
|
||||
font_small = self.parent.font_small
|
||||
self.volume_spinner = AudioPanelSpinner(
|
||||
self, font_medium, font_small, self.SLIDER_W, self.location.h, .05,
|
||||
self.get_sound_effect().local_volume,
|
||||
self.get_sound_effect().adjust_volume, self.FOREGROUND,
|
||||
self.BACKGROUND, 2, "vol")
|
||||
self.fade_out_spinner = AudioPanelSpinner(
|
||||
self, font_medium, font_small, self.SLIDER_W, self.location.h, .1,
|
||||
self.get_sound_effect().fade_out_length,
|
||||
self.get_sound_effect().adjust_fade_out_length, self.FOREGROUND,
|
||||
self.BACKGROUND, 1, "fade")
|
||||
self.loops_spinner = AudioPanelSpinner(
|
||||
self, font_medium, font_small, self.SLIDER_W, self.location.h, 1,
|
||||
self.get_sound_effect().loops,
|
||||
self.get_sound_effect().adjust_loop_count, self.FOREGROUND,
|
||||
self.BACKGROUND, 0, "loops")
|
||||
self.maxtime_spinner = AudioPanelSpinner(
|
||||
self, font_medium, font_small, self.SLIDER_W, self.location.h, .1,
|
||||
self.get_sound_effect().maxtime,
|
||||
self.get_sound_effect().adjust_maxtime, self.FOREGROUND,
|
||||
self.BACKGROUND, 1, "cutoff")
|
||||
self.play_button = AudioPanelButton(self, self.get_sound_effect().play)
|
||||
if not self.is_bgm:
|
||||
font_medium = self.parent.font_medium
|
||||
font_small = self.parent.font_small
|
||||
self.volume_spinner = AudioPanelSpinner(
|
||||
self, font_medium, font_small, self.SLIDER_W, self.location.h, .05,
|
||||
self.get_sound_effect().local_volume,
|
||||
self.get_sound_effect().adjust_volume, self.FOREGROUND,
|
||||
self.BACKGROUND, 2, "vol")
|
||||
self.fade_out_spinner = AudioPanelSpinner(
|
||||
self, font_medium, font_small, self.SLIDER_W, self.location.h, .1,
|
||||
self.get_sound_effect().fade_out_length,
|
||||
self.get_sound_effect().adjust_fade_out_length, self.FOREGROUND,
|
||||
self.BACKGROUND, 1, "fade")
|
||||
self.loops_spinner = AudioPanelSpinner(
|
||||
self, font_medium, font_small, self.SLIDER_W, self.location.h, 1,
|
||||
self.get_sound_effect().loops,
|
||||
self.get_sound_effect().adjust_loop_count, self.FOREGROUND,
|
||||
self.BACKGROUND, 0, "loops")
|
||||
self.maxtime_spinner = AudioPanelSpinner(
|
||||
self, font_medium, font_small, self.SLIDER_W, self.location.h, .1,
|
||||
self.get_sound_effect().maxtime,
|
||||
self.get_sound_effect().adjust_maxtime, self.FOREGROUND,
|
||||
self.BACKGROUND, 1, "cutoff")
|
||||
if self.is_bgm:
|
||||
callback, kwargs = self.get_game().get_audio().play_bgm, {"name": self.key}
|
||||
else:
|
||||
callback, kwargs = self.get_sound_effect().play, {}
|
||||
self.play_button = AudioPanelButton(self, callback, kwargs)
|
||||
frame = pygame.Surface((self.BUTTON_W, self.location.h), SRCALPHA)
|
||||
frame.fill(self.BACKGROUND)
|
||||
stop_button_frame = frame.copy()
|
||||
w, h = frame.get_size()
|
||||
pygame.draw.polygon(frame, self.FOREGROUND, ((w * .25, h * .25), (w * .25, h * .75), (w * .75, h * .5)))
|
||||
self.play_button.add_frame(frame)
|
||||
self.stop_button = AudioPanelButton(self, self.get_sound_effect().stop)
|
||||
if self.is_bgm:
|
||||
callback = pygame.mixer.music.stop
|
||||
else:
|
||||
callback = self.get_sound_effect().stop
|
||||
self.stop_button = AudioPanelButton(self, callback)
|
||||
stop_button_frame.fill(self.FOREGROUND, (w * .25, h * .25, w * .5, h * .5))
|
||||
self.stop_button.add_frame(stop_button_frame)
|
||||
self.stop_blinking()
|
||||
|
@ -346,8 +411,9 @@ class AudioPanelRow(BlinkingSprite):
|
|||
GameChild.unsubscribe(self, self.respond, pygame.MOUSEBUTTONDOWN)
|
||||
self.play_button.unsubscribe()
|
||||
self.stop_button.unsubscribe()
|
||||
for spinner in self.volume_spinner, self.fade_out_spinner, self.loops_spinner, self.maxtime_spinner:
|
||||
spinner.unsubscribe()
|
||||
if not self.is_bgm:
|
||||
for spinner in self.volume_spinner, self.fade_out_spinner, self.loops_spinner, self.maxtime_spinner:
|
||||
spinner.unsubscribe()
|
||||
|
||||
def build(self):
|
||||
ds = self.get_display_surface()
|
||||
|
@ -374,7 +440,11 @@ class AudioPanelRow(BlinkingSprite):
|
|||
file_sprite.location.midright = self.location.right - self.INDENT, self.location.centery
|
||||
file_sprite.display_surface = surface
|
||||
file_name_sprite = Sprite(self)
|
||||
file_name_text = self.font.render(self.get_sound_effect().path, True, self.FOREGROUND)
|
||||
if self.is_bgm:
|
||||
file_name = self.get_bgm()
|
||||
else:
|
||||
file_name = self.get_sound_effect().path
|
||||
file_name_text = self.font.render(file_name, True, self.FOREGROUND)
|
||||
file_name_sprite.add_frame(file_name_text)
|
||||
file_name_sprite.display_surface = box
|
||||
file_name_sprite.location.midright = file_sprite.location.w - self.INDENT, file_sprite.location.h / 2
|
||||
|
@ -384,31 +454,43 @@ class AudioPanelRow(BlinkingSprite):
|
|||
def get_sound_effect(self):
|
||||
return self.get_game().get_audio().sfx[self.key]
|
||||
|
||||
def get_bgm(self):
|
||||
return self.get_game().get_audio().bgm[self.key]
|
||||
|
||||
def update_config(self):
|
||||
if not self.get_configuration().has_section("sfx"):
|
||||
self.get_configuration().add_section("sfx")
|
||||
sound_effect = self.get_sound_effect()
|
||||
self.get_configuration().set(
|
||||
"sfx", self.key, "{}, {:.2f}, {:.2f}, {}, {:.2f}".format(
|
||||
if self.is_bgm:
|
||||
section_name = "bgm"
|
||||
else:
|
||||
section_name = "sfx"
|
||||
if not self.get_configuration().has_section(section_name):
|
||||
self.get_configuration().add_section(section_name)
|
||||
if self.is_bgm:
|
||||
config_value = self.get_bgm()
|
||||
else:
|
||||
sound_effect = self.get_sound_effect()
|
||||
config_value = "{}, {:.2f}, {:.2f}, {}, {:.2f}".format(
|
||||
sound_effect.path, sound_effect.local_volume, sound_effect.fade_out_length,
|
||||
sound_effect.loops, sound_effect.maxtime))
|
||||
sound_effect.loops, sound_effect.maxtime)
|
||||
self.get_configuration().set(section_name, self.key, config_value)
|
||||
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"))
|
||||
|
||||
def update(self):
|
||||
self.volume_spinner.location.midleft = self.location.move(5, 0).midright
|
||||
self.fade_out_spinner.location.midleft = self.volume_spinner.location.midright
|
||||
self.loops_spinner.location.midleft = self.fade_out_spinner.location.midright
|
||||
self.maxtime_spinner.location.midleft = self.loops_spinner.location.midright
|
||||
self.play_button.location.midleft = self.maxtime_spinner.location.move(5, 0).midright
|
||||
self.play_button.location.midleft = self.location.move(5, 0).midright
|
||||
self.stop_button.location.midleft = self.play_button.location.midright
|
||||
if not self.is_bgm:
|
||||
self.volume_spinner.location.midleft = self.stop_button.location.move(5, 0).midright
|
||||
self.fade_out_spinner.location.midleft = self.volume_spinner.location.midright
|
||||
self.loops_spinner.location.midleft = self.fade_out_spinner.location.midright
|
||||
self.maxtime_spinner.location.midleft = self.loops_spinner.location.midright
|
||||
Sprite.update(self)
|
||||
self.volume_spinner.update()
|
||||
self.fade_out_spinner.update()
|
||||
self.loops_spinner.update()
|
||||
self.maxtime_spinner.update()
|
||||
if not self.is_bgm:
|
||||
self.volume_spinner.update()
|
||||
self.fade_out_spinner.update()
|
||||
self.loops_spinner.update()
|
||||
self.maxtime_spinner.update()
|
||||
self.play_button.update()
|
||||
self.stop_button.update()
|
||||
|
||||
|
@ -422,7 +504,9 @@ class AudioPanelFileBrowser(Sprite):
|
|||
|
||||
def __init__(self, parent):
|
||||
Sprite.__init__(self, parent)
|
||||
self.rows = []
|
||||
self.font = self.parent.font_large
|
||||
self.previewing_sound = None
|
||||
ds = self.get_display_surface()
|
||||
dsr = ds.get_rect()
|
||||
surface = pygame.Surface((dsr.w * self.WIDTH - 2, dsr.h * self.HEIGHT - 2), SRCALPHA)
|
||||
|
@ -434,7 +518,9 @@ class AudioPanelFileBrowser(Sprite):
|
|||
self.subscribe(self.respond, pygame.MOUSEBUTTONDOWN)
|
||||
|
||||
def reset(self):
|
||||
self.visit(self.HOME)
|
||||
if self.previewing_sound is not None:
|
||||
self.previewing_sound.stop()
|
||||
# self.visit(self.HOME)
|
||||
self.hide()
|
||||
|
||||
def respond(self, event):
|
||||
|
@ -442,14 +528,20 @@ class AudioPanelFileBrowser(Sprite):
|
|||
if event.button == 1:
|
||||
if self.collide(event.pos):
|
||||
for row in self.rows:
|
||||
if row.collide(Vector(*event.pos).get_moved(-self.location.left, -self.location.top)):
|
||||
pos = Vector(*event.pos).get_moved(-self.location.left, -self.location.top)
|
||||
if (not row.has_child("button") or pos.x < row.get_child("button").location.left) and row.collide(pos):
|
||||
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):
|
||||
loaded = False
|
||||
selected = self.parent.get_selected()
|
||||
if self.get_audio().load_sfx_file(full_path, selected.key, True):
|
||||
if selected.is_bgm:
|
||||
loaded = self.get_audio().set_bgm(full_path, selected.key)
|
||||
else:
|
||||
loaded = self.get_audio().load_sfx_file(full_path, selected.key, True)
|
||||
if loaded:
|
||||
selected.update_config()
|
||||
self.hide()
|
||||
self.get_delegate().cancel_propagation()
|
||||
|
@ -466,8 +558,24 @@ class AudioPanelFileBrowser(Sprite):
|
|||
for row in self.parent.rows:
|
||||
row.selected = False
|
||||
row.stop_blinking()
|
||||
row.play_button.set_clickable()
|
||||
row.stop_button.set_clickable()
|
||||
for row in self.rows:
|
||||
if row.has_child("button"):
|
||||
row.get_child("button").set_clickable(False)
|
||||
if self.previewing_sound is not None:
|
||||
self.previewing_sound.stop()
|
||||
Sprite.hide(self)
|
||||
|
||||
def unhide(self):
|
||||
for row in self.parent.rows:
|
||||
row.play_button.set_clickable(False)
|
||||
row.stop_button.set_clickable(False)
|
||||
for row in self.rows:
|
||||
if row.has_child("button"):
|
||||
row.get_child("button").set_clickable()
|
||||
Sprite.unhide(self)
|
||||
|
||||
def visit(self, path):
|
||||
if path == self.UP and len(self.trail) > 1:
|
||||
path = self.trail[-2]
|
||||
|
@ -478,7 +586,8 @@ class AudioPanelFileBrowser(Sprite):
|
|||
if path == self.HOME:
|
||||
self.trail = []
|
||||
self.paths = ["/"]
|
||||
for option in "sfx-repository-path", "sfx-default-path", "sfx-project-path":
|
||||
for option in "sfx-repository-path", "sfx-default-path", "sfx-project-path", \
|
||||
"bgm-repository-path", "bgm-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))
|
||||
|
@ -491,6 +600,10 @@ class AudioPanelFileBrowser(Sprite):
|
|||
self.build()
|
||||
|
||||
def build(self):
|
||||
for row in self.rows:
|
||||
if row.has_child("button"):
|
||||
row.get_child("button").unsubscribe()
|
||||
del row
|
||||
self.rows = []
|
||||
for path in self.paths:
|
||||
row = Sprite(self)
|
||||
|
@ -498,10 +611,29 @@ class AudioPanelFileBrowser(Sprite):
|
|||
text = self.font.render(path, True, self.COLORS[1])
|
||||
surface = pygame.Surface((self.location.w, text.get_height()), SRCALPHA)
|
||||
surface.blit(text, (0, 0))
|
||||
surface.fill(self.COLORS[1], (0, surface.get_height() - 1, self.location.w, 1))
|
||||
row.add_frame(surface)
|
||||
row.display_surface = self.get_current_frame()
|
||||
row.location.bottom = 0
|
||||
self.rows.append(row)
|
||||
full_path = os.path.join(os.path.sep.join(self.trail), path)
|
||||
if self.get_audio().is_sound_file(full_path):
|
||||
button = AudioPanelButton(self, self.preview, {"path": full_path}, [row, self])
|
||||
row.set_child("button", button)
|
||||
frame = pygame.Surface([text.get_height()] * 2, SRCALPHA)
|
||||
w, h = frame.get_size()
|
||||
pygame.draw.polygon(
|
||||
frame, self.COLORS[1], ((w * .25, h * .25), (w * .25, h * .75), (w * .75, h * .5)))
|
||||
button.add_frame(frame)
|
||||
button.display_surface = row.get_current_frame()
|
||||
button.location.right = self.location.w
|
||||
|
||||
def preview(self, path):
|
||||
if self.get_audio().is_sound_file(path):
|
||||
if self.previewing_sound is not None:
|
||||
self.previewing_sound.stop()
|
||||
self.previewing_sound = SoundEffect(self, path)
|
||||
self.previewing_sound.play()
|
||||
|
||||
def update(self):
|
||||
self.get_current_frame().blit(self.background, (0, 0))
|
||||
|
@ -609,9 +741,12 @@ class AudioPanelSpinner(Sprite):
|
|||
|
||||
class AudioPanelButton(Sprite):
|
||||
|
||||
def __init__(self, parent, callback):
|
||||
def __init__(self, parent, callback, callback_kwargs={}, containers=[]):
|
||||
Sprite.__init__(self, parent)
|
||||
self.callback = callback
|
||||
self.callback_kwargs = callback_kwargs
|
||||
self.containers = containers
|
||||
self.clickable = True
|
||||
self.subscribe(self.respond, pygame.MOUSEBUTTONDOWN)
|
||||
|
||||
def unsubscribe(self, callback=None, kind=None):
|
||||
|
@ -621,5 +756,12 @@ class AudioPanelButton(Sprite):
|
|||
Sprite.unsubscribe(self, callback, kind)
|
||||
|
||||
def respond(self, event):
|
||||
if event.button == 1 and self.collide(event.pos):
|
||||
self.callback()
|
||||
if self.clickable and event.button == 1:
|
||||
pos = Vector(*event.pos)
|
||||
for container in self.containers:
|
||||
pos.move(-container.location.left, -container.location.top)
|
||||
if self.collide(pos):
|
||||
self.callback(**self.callback_kwargs)
|
||||
|
||||
def set_clickable(self, clickable=True):
|
||||
self.clickable = clickable
|
||||
|
|
|
@ -89,6 +89,7 @@ class Configuration(RawConfigParser):
|
|||
section = "input"
|
||||
add_section(section)
|
||||
set_option(section, "release-suffix", "-release", False)
|
||||
set_option(section, "confirm-quit", "no", False)
|
||||
section = "sprite"
|
||||
add_section(section)
|
||||
set_option(section, "transparent-color", "magenta", False)
|
||||
|
@ -124,7 +125,7 @@ class Configuration(RawConfigParser):
|
|||
set_option(section, "volume-up", "K_F2", False)
|
||||
set_option(section, "volume-mute", "K_F3", False)
|
||||
set_option(section, "toggle-interpolator", "K_F7", False)
|
||||
set_option(section, "toggle-audio-panel", "K_a", False)
|
||||
set_option(section, "toggle-audio-panel", "K_F6", False)
|
||||
section = "joy"
|
||||
add_section(section)
|
||||
set_option(section, "advance", "7", False)
|
||||
|
@ -145,8 +146,10 @@ class Configuration(RawConfigParser):
|
|||
set_option(section, "sfx-default-path", "~/storage/audio/sfx/default", False)
|
||||
set_option(section, "sfx-repository-path", "~/storage/audio/sfx/all", False)
|
||||
set_option(section, "sfx-project-path", "sfx", False)
|
||||
set_option(section, "sfx-volume", "1.0", False)
|
||||
set_option(section, "sfx-extensions", "wav, ogg, mp3", False)
|
||||
set_option(section, "bgm-repository-path", "~/storage/audio/bgm", False)
|
||||
set_option(section, "bgm-project-path", "bgm", False)
|
||||
set_option(section, "sfx-volume", "1.0", False)
|
||||
set_option(section, "panel-font", None, False)
|
||||
section = "interpolator-gui"
|
||||
add_section(section)
|
||||
|
@ -407,6 +410,8 @@ class TypeDeclarations(dict):
|
|||
"int-list": ["dimensions", "framerate-text-color",
|
||||
"framerate-text-background"]},
|
||||
|
||||
"input": {"bool": "confirm-quit"},
|
||||
|
||||
"screen-captures": {"path": ["rel-path", "path"]},
|
||||
|
||||
"video-recordings": {"path": ["rel-path", "path"],
|
||||
|
@ -443,7 +448,8 @@ class TypeDeclarations(dict):
|
|||
"path": "panel-font",
|
||||
|
||||
"path-list": [
|
||||
"sfx-default-path", "sfx-repository-path", "sfx-project-path"
|
||||
"sfx-default-path", "sfx-repository-path", "sfx-project-path",
|
||||
"bgm-repository-path", "bgm-project-path"
|
||||
],
|
||||
|
||||
"float": "sfx-volume"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import collections
|
||||
|
||||
from pygame.event import get, pump, Event, post
|
||||
from pygame.locals import *
|
||||
|
||||
|
@ -8,7 +10,7 @@ class Delegate(GameChild):
|
|||
|
||||
def __init__(self, game):
|
||||
GameChild.__init__(self, game)
|
||||
self.subscribers = dict()
|
||||
self.subscribers = collections.OrderedDict()
|
||||
self.load_configuration()
|
||||
self.disable()
|
||||
self.cancelling_propagation = False
|
||||
|
|
51
pgfw/Game.py
51
pgfw/Game.py
|
@ -13,12 +13,16 @@ from .Profile import Profile
|
|||
from .VideoRecorder import VideoRecorder
|
||||
from .Interpolator import Interpolator
|
||||
from .TimeFilter import TimeFilter
|
||||
from .Sprite import Sprite
|
||||
from .confirm_quit_message import CONFIRM_QUIT_MESSAGE
|
||||
from .extension import *
|
||||
|
||||
class Game(Animation):
|
||||
|
||||
resource_path = None
|
||||
|
||||
def __init__(self, config_rel_path=None, type_declarations=None):
|
||||
self.confirming_quit = False
|
||||
self.profile = Profile(self)
|
||||
self.time_filter = TimeFilter(self)
|
||||
Animation.__init__(self, self)
|
||||
|
@ -32,8 +36,6 @@ class Game(Animation):
|
|||
os.putenv("SDL_NOMOUSE", "1")
|
||||
pygame.init()
|
||||
self.set_children()
|
||||
self.subscribe(self.end, QUIT)
|
||||
self.subscribe(self.end)
|
||||
self.register(self.get_input().unsuppress)
|
||||
self.delegate.enable()
|
||||
|
||||
|
@ -44,6 +46,8 @@ class Game(Animation):
|
|||
|
||||
def set_children(self):
|
||||
self.delegate = Delegate(self)
|
||||
self.subscribe(self.end, QUIT)
|
||||
self.subscribe(self.end)
|
||||
self.display = Display(self)
|
||||
self.mainloop = Mainloop(self)
|
||||
self.input = Input(self)
|
||||
|
@ -57,13 +61,37 @@ class Game(Animation):
|
|||
self.delegate.dispatch()
|
||||
if not self.interpolator.is_gui_active():
|
||||
Animation.update(self)
|
||||
self.update()
|
||||
if self.confirming_quit:
|
||||
self.time_filter.close()
|
||||
self.draw_confirm_quit_dialog()
|
||||
else:
|
||||
self.time_filter.open()
|
||||
self.update()
|
||||
else:
|
||||
self.interpolator.gui.update()
|
||||
self.audio.update()
|
||||
if self.video_recorder.requested:
|
||||
self.video_recorder.update()
|
||||
|
||||
def draw_confirm_quit_dialog(self):
|
||||
dialog = Sprite(self)
|
||||
dialog.add_frame(self.get_confirm_quit_surface())
|
||||
dialog.location.center = self.get_display_surface().get_rect().center
|
||||
dialog.update()
|
||||
|
||||
def get_confirm_quit_surface(self):
|
||||
lines = CONFIRM_QUIT_MESSAGE.strip().split("\n")
|
||||
w, h = len(lines[0]), len(lines)
|
||||
text = pygame.Surface((w, h), SRCALPHA)
|
||||
for y, line in enumerate(lines):
|
||||
for x, char in enumerate(line.strip()):
|
||||
if char == "#":
|
||||
text.set_at((x, y), (0, 0, 0))
|
||||
text = pygame.transform.scale2x(text)
|
||||
text = pygame.transform.scale2x(text)
|
||||
return get_boxed_surface(
|
||||
text, background=(255, 255, 255), border=(0, 0, 0), border_width=24, padding=12)
|
||||
|
||||
def run(self):
|
||||
self.mainloop.run()
|
||||
|
||||
|
@ -80,7 +108,16 @@ class Game(Animation):
|
|||
self.get_input().suppress()
|
||||
self.play(self.get_input().unsuppress, delay=length, play_once=True)
|
||||
|
||||
def end(self, evt):
|
||||
if evt.type == QUIT or self.delegate.compare(evt, "quit"):
|
||||
self.mainloop.stop()
|
||||
self.profile.end()
|
||||
def end(self, event):
|
||||
if event.type == QUIT or self.delegate.compare(event, "quit"):
|
||||
if self.confirming_quit or not self.get_configuration("input", "confirm-quit"):
|
||||
self.mainloop.stop()
|
||||
self.profile.end()
|
||||
else:
|
||||
print("press quit again to confirm or press any other button to cancel...")
|
||||
self.confirming_quit = True
|
||||
elif self.delegate.compare(event, "any"):
|
||||
if self.confirming_quit and self.delegate.compare(event, "any"):
|
||||
print("cancelled quit...")
|
||||
self.confirming_quit = False
|
||||
self.delegate.cancel_propagation()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from time import time as get_secs
|
||||
|
||||
from pygame import joystick as joy
|
||||
from pygame.key import get_pressed
|
||||
from pygame.key import get_pressed, get_mods
|
||||
from pygame.locals import *
|
||||
|
||||
from .GameChild import *
|
||||
|
@ -18,6 +18,7 @@ class Input(GameChild):
|
|||
self.load_configuration()
|
||||
self.set_any_press_ignore_list()
|
||||
self.unsuppress()
|
||||
self.unsuppress_any_on_mods()
|
||||
self.subscribe_to_events()
|
||||
self.build_key_map()
|
||||
self.build_joy_button_map()
|
||||
|
@ -31,12 +32,19 @@ class Input(GameChild):
|
|||
def set_any_press_ignore_list(self):
|
||||
self.any_press_ignored = set(["capture-screen", "toggle-fullscreen",
|
||||
"reset-game", "record-video", "quit",
|
||||
"mute", "toggle-interpolator"])
|
||||
"toggle-interpolator", "toggle-audio-panel",
|
||||
"volume-down", "volume-up", "volume-mute"])
|
||||
self.any_press_ignored_keys = set()
|
||||
|
||||
def unsuppress(self):
|
||||
self.suppressed = False
|
||||
|
||||
def unsuppress_any_on_mods(self):
|
||||
self.suppressed_any_key_on_mods = False
|
||||
|
||||
def suppress_any_key_on_mods(self):
|
||||
self.suppressed_any_key_on_mods = True
|
||||
|
||||
def subscribe_to_events(self):
|
||||
self.subscribe(self.translate_key, KEYDOWN)
|
||||
self.subscribe(self.translate_key, KEYUP)
|
||||
|
@ -72,7 +80,9 @@ class Input(GameChild):
|
|||
self.post_command(cmd, cancel=cancel)
|
||||
posted = cmd
|
||||
if (not posted or posted not in self.any_press_ignored) and \
|
||||
key not in self.any_press_ignored_keys:
|
||||
key not in self.any_press_ignored_keys and \
|
||||
(not self.suppressed_any_key_on_mods or \
|
||||
(not get_mods() & KMOD_CTRL and not get_mods() & KMOD_ALT)):
|
||||
self.post_any_command(key, cancel)
|
||||
|
||||
def post_command(self, cmd, **attributes):
|
||||
|
|
|
@ -25,6 +25,7 @@ class Sprite(Animation):
|
|||
self.neighbors = neighbors
|
||||
self.mass = mass
|
||||
self._step = EVector()
|
||||
self.children = {}
|
||||
self.set_frameset(0)
|
||||
self.locations.append(Location(self))
|
||||
self.motion_overflow = Vector()
|
||||
|
@ -38,6 +39,15 @@ class Sprite(Animation):
|
|||
return Animation.__getattr__(self, name)
|
||||
raise AttributeError(name)
|
||||
|
||||
def add_frameset(self, order=[], framerate=None, name=None, switch=False):
|
||||
if framerate is None:
|
||||
framerate = self.accounts[self.default_method].interval
|
||||
frameset = Frameset(self, order, framerate, name)
|
||||
self.framesets.append(frameset)
|
||||
if switch:
|
||||
self.set_frameset(len(self.framesets) - 1)
|
||||
return frameset
|
||||
|
||||
def set_frameset(self, identifier):
|
||||
if isinstance(identifier, str):
|
||||
for ii, frameset in enumerate(self.framesets):
|
||||
|
@ -109,6 +119,10 @@ class Sprite(Animation):
|
|||
if frameset.length() > 1:
|
||||
self.play()
|
||||
|
||||
def add_frames(self, frames):
|
||||
for frame in frames:
|
||||
self.add_frame(frame)
|
||||
|
||||
def shift_frame(self):
|
||||
self.get_current_frameset().shift()
|
||||
|
||||
|
@ -188,13 +202,6 @@ class Sprite(Animation):
|
|||
for location in self.locations:
|
||||
location.fader.set_alpha()
|
||||
|
||||
def add_frameset(self, order=[], framerate=None, name=None, switch=False):
|
||||
frameset = Frameset(self, order, framerate, name)
|
||||
self.framesets.append(frameset)
|
||||
if switch:
|
||||
self.set_frameset(len(self.framesets) - 1)
|
||||
return frameset
|
||||
|
||||
def hide(self):
|
||||
for location in self.locations:
|
||||
location.hide()
|
||||
|
@ -232,6 +239,15 @@ class Sprite(Animation):
|
|||
def is_stepping(self):
|
||||
return bool(self._step)
|
||||
|
||||
def set_child(self, name, child):
|
||||
self.children[name] = child
|
||||
|
||||
def get_child(self, name):
|
||||
return self.children[name]
|
||||
|
||||
def has_child(self, name):
|
||||
return name in self.children
|
||||
|
||||
def update(self, areas=None, substitute=None, flags=0):
|
||||
Animation.update(self)
|
||||
if self.is_stepping():
|
||||
|
@ -241,6 +257,8 @@ class Sprite(Animation):
|
|||
neighbor.mass))
|
||||
if self.get_current_frameset().length():
|
||||
self.draw(areas, substitute, flags)
|
||||
for child in self.children.values():
|
||||
child.update()
|
||||
# for location in self.locations:
|
||||
# location.update()
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
CONFIRM_QUIT_MESSAGE = '''
|
||||
............................................................
|
||||
..###..###..#....#.####.#.####..#...#....###..#...#.#.#####.
|
||||
.#....#...#.##...#.#....#.#...#.##.##...#...#.#...#.#...#...
|
||||
.#....#...#.#.#..#.####.#.#...#.#.#.#...#...#.#...#.#...#...
|
||||
.#....#...#.#..#.#.#....#.####..#...#...#.#.#.#...#.#...#...
|
||||
.#....#...#.#...##.#....#.#...#.#...#...#..#..#...#.#...#...
|
||||
..###..###..#....#.#....#.#...#.#...#....##.#..###..#...#...
|
||||
............................................................
|
||||
'''
|
|
@ -1,12 +1,20 @@
|
|||
import itertools, random
|
||||
from math import sin, cos, atan2, radians, sqrt, pi
|
||||
|
||||
from pygame import Surface, PixelArray, Color, Rect, draw
|
||||
from pygame import Surface, PixelArray, Color, Rect, draw, gfxdraw
|
||||
from pygame.mixer import get_num_channels, Channel
|
||||
from pygame.locals import *
|
||||
|
||||
from .Vector import Vector
|
||||
|
||||
def clamp(n, min_n, max_n):
|
||||
if n < min_n:
|
||||
return min_n
|
||||
elif n > max_n:
|
||||
return max_n
|
||||
else:
|
||||
return n
|
||||
|
||||
def get_step(start, end, speed):
|
||||
x0, y0 = start
|
||||
x1, y1 = end
|
||||
|
@ -352,6 +360,27 @@ def get_lightened_color(color, lightness):
|
|||
h, s, _, a = color.hsla
|
||||
return get_hsla_color(h, s, lightness, a)
|
||||
|
||||
def get_glow_frames(radius, segments, colors=[(0, 0, 0), (255, 255, 255)], minsize=4, transparency=True):
|
||||
frames = []
|
||||
radius = int(round(radius))
|
||||
sizes = [int(round(minsize + float(ii) / (segments - 1) * (radius - minsize))) for ii in range(segments)]
|
||||
if transparency:
|
||||
alpha_step = 255.0 / segments
|
||||
alpha = alpha_step
|
||||
else:
|
||||
alpha = 255
|
||||
for color_offset in range(len(colors)):
|
||||
frame = Surface([radius * 2] * 2, SRCALPHA if transparency else None)
|
||||
if transparency:
|
||||
alpha = alpha_step
|
||||
for segment_ii, segment_radius in enumerate(reversed(sizes)):
|
||||
color = Color(*(colors[(color_offset + segment_ii) % len(colors)] + (int(round(alpha)),)))
|
||||
gfxdraw.filled_circle(frame, radius, radius, int(round(segment_radius)), color)
|
||||
if transparency:
|
||||
alpha += alpha_step
|
||||
frames.append(frame)
|
||||
return frames
|
||||
|
||||
# http://www.pygame.org/wiki/BezierCurve
|
||||
def compute_bezier_points(vertices, numPoints=60):
|
||||
points = []
|
||||
|
|
Loading…
Reference in New Issue