chord; aa_filled

This commit is contained in:
Frank DeMarco 2016-07-02 17:16:42 -04:00
commit 004b577f9f
14 changed files with 340 additions and 114 deletions

View File

@ -1,92 +1,50 @@
from os import listdir
from os.path import join
from pygame.mixer import Channel, Sound, music, find_channel
from pygame.mixer import Channel, Sound, music, find_channel, get_num_channels
from GameChild import *
from Input import *
class Audio(GameChild):
current_channel = None
paused = False
muted = False
UP, DOWN = .1, -.1
BASE_VOLUME = .8
def __init__(self, game):
GameChild.__init__(self, game)
self.delegate = self.get_delegate()
self.load_fx()
self.original_volumes = {}
self.volume = self.BASE_VOLUME
if self.check_command_line("-mute"):
self.volume = 0
self.subscribe(self.respond)
def load_fx(self):
fx = {}
if self.get_configuration().has_option("audio", "sfx-path"):
root = self.get_resource("audio", "sfx-path")
if root:
for name in listdir(root):
fx[name.split(".")[0]] = Sound(join(root, name))
self.fx = fx
def set_volume(self, volume=None, increment=None, mute=False):
if mute:
self.volume = 0
elif increment:
self.volume += increment
if self.volume > 1:
self.volume = 1.0
elif self.volume < 0:
self.volume = 0
else:
self.volume = volume
def respond(self, event):
if self.delegate.compare(event, "mute"):
self.mute()
compare = self.get_game().delegate.compare
if compare(event, "volume-mute"):
self.set_volume(mute=True)
elif compare(event, "volume-up"):
self.set_volume(increment=self.UP)
elif compare(event, "volume-down"):
self.set_volume(increment=self.DOWN)
def mute(self):
self.muted = True
self.set_volume()
def unmute(self):
self.muted = False
self.set_volume()
def set_volume(self):
volume = int(not self.muted)
music.set_volume(volume)
if self.current_channel:
self.current_channel.set_volume(volume)
def play_bgm(self, path, stream=False):
self.stop_current_channel()
if stream:
music.load(path)
music.play(-1)
else:
self.current_channel = Sound(path).play(-1)
self.set_volume()
def stop_current_channel(self):
music.stop()
if self.current_channel:
self.current_channel.stop()
self.current_channel = None
self.paused = False
def play_fx(self, name, panning=.5):
if not self.muted:
channel = find_channel(True)
if panning != .5:
offset = 1 - abs(panning - .5) * 2
if panning < .5:
channel.set_volume(1, offset)
else:
channel.set_volume(offset, 1)
channel.play(self.fx[name])
def pause(self):
channel = self.current_channel
paused = self.paused
if paused:
music.unpause()
if channel:
channel.unpause()
else:
music.pause()
if channel:
channel.pause()
self.paused = not paused
def is_bgm_playing(self):
current = self.current_channel
if current and current.get_sound():
return True
return music.get_busy()
def update(self):
for ii in xrange(get_num_channels()):
channel = Channel(ii)
sound = channel.get_sound()
if sound is not None:
if sound not in self.original_volumes.keys():
self.original_volumes[sound] = sound.get_volume()
sound.set_volume(self.original_volumes[sound] * self.volume)

View File

@ -65,6 +65,7 @@ class Configuration(RawConfigParser):
set_option(section, "windows-dist-path", "dist/win/", False)
set_option(section, "windows-icon-path", "", False)
set_option(section, "lowercase-boolean-true", "yes", False)
set_option(section, "osx-includes", "", False)
section = "display"
add_section(section)
set_option(section, "dimensions", "480, 360", False)
@ -106,15 +107,17 @@ class Configuration(RawConfigParser):
set_option(section, "double-click-time-limit", ".5", False)
section = "keys"
add_section(section)
set_option(section, "up", "K_UP, K_w", False)
set_option(section, "right", "K_RIGHT, K_d", False)
set_option(section, "down", "K_DOWN, K_s", False)
set_option(section, "left", "K_LEFT, K_a", False)
set_option(section, "up", "K_UP", False)
set_option(section, "right", "K_RIGHT", False)
set_option(section, "down", "K_DOWN", False)
set_option(section, "left", "K_LEFT", False)
set_option(section, "capture-screen", "K_F9", False)
set_option(section, "toggle-fullscreen", "K_F11", False)
set_option(section, "reset-game", "K_F8", False)
set_option(section, "record-video", "K_F10", False)
set_option(section, "mute", "K_F12", False)
set_option(section, "volume-down", "K_F1", False)
set_option(section, "volume-up", "K_F2", False)
set_option(section, "volume-mute", "K_F3", False)
set_option(section, "toggle-interpolator", "K_F7", False)
section = "joy"
add_section(section)
@ -295,10 +298,11 @@ class Configuration(RawConfigParser):
exclude = []
if self.has_option(section, option):
exclude = self.get(section, option)
exclude += [".git", ".gitignore", "README", "build/", "dist/",
"setup.py", "MANIFEST", "PKG-INFO",
exclude += [".git*", "README", "build/", "dist/", "*.egg-info",
"*.py", "MANIFEST*", "PKG-INFO", "*.pyc", "*.swp", "*~",
self.get("setup", "changelog"),
self.get("setup", "package-root")]
self.get("setup", "package-root"),
self.get("setup", "init-script")]
for location in self.get("setup", "additional-packages"):
exclude.append(location)
self.set(section, option, exclude, False)
@ -395,7 +399,7 @@ class TypeDeclarations(dict):
"setup": {"list": ["classifiers", "resource-search-path",
"requirements", "data-exclude",
"additional-packages"],
"additional-packages", "osx-includes"],
"path": ["installation-dir", "changelog", "description-file",
"main-object", "icon-path", "windows-dist-path",

View File

@ -79,7 +79,7 @@ class Delegate(GameChild):
return self.get_command_attribute(evt) in commands
def get_command_attribute(self, evt):
return evt.dict[self.command_key]
return evt.dict.has_key(self.command_key) and evt.dict[self.command_key]
def post(self, command=None, cancel=False, **attributes):
attributes[self.command_key] = command

View File

@ -54,6 +54,7 @@ class Game(GameChild):
self.update()
else:
self.interpolator.gui.update()
self.audio.update()
if self.video_recorder.requested:
self.video_recorder.update()

View File

@ -94,7 +94,7 @@ class Input(GameChild):
if not self.suppressed and not self.check_command_line("-disable-joy-axis"):
axis = event.axis
value = event.value
if not value:
if -.01 < value < .01:
for command in "up", "right", "down", "left":
self.post_command(command, cancel=True)
if command not in self.any_press_ignored:

View File

@ -404,7 +404,7 @@ class GUI(Animation):
def deactivate(self):
self.active = False
self.time_filter.open()
self.audio.muted = self.saved_mute_state
# self.audio.muted = self.saved_mute_state
self.display.set_mouse_visibility(self.saved_mouse_state)
def respond_to_command(self, event):
@ -427,8 +427,8 @@ class GUI(Animation):
def activate(self):
self.active = True
self.time_filter.close()
self.saved_mute_state = self.audio.muted
self.audio.mute()
# self.saved_mute_state = self.audio.muted
# self.audio.mute()
self.draw()
self.saved_mouse_state = self.display.set_mouse_visibility(True)

View File

@ -138,3 +138,7 @@ class Chord:
fade_in = [fade_in]
for ii, note in enumerate(self.notes):
note.play(maxtime, fadeout[ii % len(fadeout)], panning, fade_in[ii % len(fade_in)])
def stop(self):
for note in self.notes:
note.stop()

View File

@ -1,11 +1,12 @@
from os import walk, remove
from os.path import sep, join, exists, normpath
from os.path import sep, join, exists, normpath, basename
from re import findall, sub
from distutils.core import setup
from distutils.command.install import install
from pprint import pprint
from fileinput import FileInput
from re import sub, match
from fnmatch import fnmatch
from Configuration import *
@ -29,7 +30,8 @@ class Setup:
for location in locations:
if exists(location):
for root, dirs, files in walk(location, followlinks=True):
packages.append(root.replace(sep, "."))
if exists(join(root, "__init__.py")):
packages.append(root.replace(sep, "."))
return packages
def build_data_map(self):
@ -48,12 +50,16 @@ class Setup:
def remove_excluded(self, paths, root, exclude):
removal = []
for path in paths:
if normpath(join(root, path)) in exclude:
if self.contains_path(join(root, path), exclude):
removal.append(path)
for path in removal:
paths.remove(path)
if path in paths:
paths.remove(path)
return paths
def contains_path(self, path, container):
return any(fnmatch(path, rule) or fnmatch(basename(path), rule) for rule in container)
def translate_title(self):
config = self.config.get_section("setup")
title = config["title"].replace(" ", config["whitespace-placeholder"])

27
pgfw/SetupOSX.py Normal file
View File

@ -0,0 +1,27 @@
from setuptools import setup, find_packages
from Configuration import Configuration
from Setup import Setup
class SetupOSX(Setup):
def __init__(self):
Setup.__init__(self)
def setup(self):
config = Configuration()
setup_section = config.get_section("setup")
version = setup_section["version"]
name = self.translate_title()
plist = dict(
CFBundleIconFile=name,
CFBundleName=name,
CFBundleShortVersionString=version,
CFBundleGetInfoString=' '.join([name, version]),
CFBundleExecutable=name,
CFBundleIdentifier='org.' + name.lower())
setup(name=name,
version=version,
app=[dict(script=setup_section["init-script"], plist=plist)],
setup_requires=["py2app"],
options=dict(py2app=dict(arch="i386",)),
data_files=setup_section["osx-includes"])

View File

@ -9,21 +9,25 @@ from pygame.transform import flip
from pygame.locals import *
from Animation import Animation
from Vector import Vector
from Vector import Vector, EVector
from extension import get_hue_shifted_surface, get_step
class Sprite(Animation):
def __init__(self, parent, framerate=None):
def __init__(self, parent, framerate=None, neighbors=[], mass=None):
Animation.__init__(self, parent, self.shift_frame, framerate)
self.frames = []
self.mirrored = False
self.alpha = 255
self.locations = []
self.framesets = [Frameset(self, framerate=framerate)]
self.frameset_index = 0
self.neighbors = neighbors
self.mass = mass
self.step = EVector()
self.set_frameset(0)
self.locations.append(Location(self))
self.motion_overflow = Vector()
self.stop()
self.display_surface = self.get_display_surface()
def __getattr__(self, name):
@ -39,11 +43,13 @@ class Sprite(Animation):
if frameset.name == identifier:
identifier = ii
break
self.frameset_index = identifier
self.register_interval()
self.update_location_size()
if self.get_current_frameset().length() > 1:
self.play()
if self.frameset_index != identifier:
self.frameset_index = identifier
self.register_interval()
self.update_location_size()
if self.get_current_frameset().length() > 1:
self.play()
self.get_current_frameset().reset()
def register_interval(self):
self.register(self.shift_frame,
@ -204,21 +210,26 @@ class Sprite(Animation):
for frameset in self.framesets:
frameset.reverse()
def go(self, dx=0, dy=0):
self.go_vector = Vector(dx, dy)
def set_step(self, dx=0, dy=0, magnitude=None, angle=0):
self.step.set_step(dx, dy, magnitude, angle)
def stop(self):
self.go_vector = Vector()
def cancel_step(self):
self.step.set_step(0, 0)
def is_going(self):
return self.go_vector != [0, 0]
def is_stepping(self):
return bool(self.step)
def update(self, areas=None, substitute=None):
Animation.update(self)
if self.is_going():
self.move(*self.go_vector)
if self.is_stepping():
self.move(self.step.dx, self.step.dy)
for neighbor in self.neighbors:
self.move(*get_step(self.location.center, neighbor.location.center,
neighbor.mass))
if self.get_current_frameset().length():
self.draw(areas, substitute)
# for location in self.locations:
# location.update()
def draw(self, areas=None, substitute=None):
for location in self.locations:
@ -249,6 +260,10 @@ class Location(Rect):
overflow[1] -= int(overflow[1])
return excess
def move_to(self, x, y, base=None):
ox, oy = self.apply_motion_overflow(base)
self.move_ip(x - ox, y - oy)
def reset_motion_overflow(self):
self.motion_overflow.place_at_origin()
@ -269,6 +284,18 @@ class Location(Rect):
def is_hidden(self):
return self.hidden
def update(self):
pass
# for neighbor in self.sprite.neighbors:
# if neighbor.mass:
# closest = neighbor.location
# for location in neighbor.locations[1:]:
# if get_distance(self.center, location.center) < \
# get_distance(self.center, closest.center):
# closest = location
# self.move_ip(get_step(self.center, closest.center,
# neighbor.mass))
class Fader(Surface):
@ -427,3 +454,25 @@ class Frameset:
def reverse(self):
self.reversed = not self.reversed
class BlinkingSprite(Sprite):
def __init__(self, parent, blink_rate, framerate=None):
Sprite.__init__(self, parent, framerate)
self.register(self.blink, interval=blink_rate)
self.play(self.blink)
def reset(self):
self.unhide()
def blink(self):
self.toggle_hidden()
class RainbowSprite(Sprite):
def __init__(self, parent, image, hue_shift=8, framerate=None):
Sprite.__init__(self, parent, framerate)
for hue in xrange(0, 360, hue_shift):
self.add_frame(get_hue_shifted_surface(image, hue))

View File

@ -6,9 +6,12 @@ class TimeFilter(GameChild):
def __init__(self, parent):
GameChild.__init__(self, parent)
self.ticks = self.unfiltered_ticks = self.last_ticks = get_ticks()
self.reset_ticks()
self.open()
def reset_ticks(self):
self.ticks = self.unfiltered_ticks = self.last_ticks = get_ticks()
def close(self):
self.closed = True

View File

@ -1,8 +1,18 @@
from math import pi, degrees
from extension import get_delta, get_distance, get_angle
class Vector(list):
def __init__(self, x=0, y=0):
list.__init__(self, (x, y))
def __repr__(self):
message = "<%f" % self[0]
for value in self[1:]:
message += ", %f" % value
return message + ">"
def __getattr__(self, name):
if name == "x":
return self[0]
@ -48,6 +58,24 @@ class Vector(list):
self.y *= other
return self
def __eq__(self, other):
for sv, ov in zip(self, other):
if value != other[ii]:
return False
return True
def __ne__(self, other):
for sv, ov in zip(self, other):
if value == other[ii]:
return False
return True
def __nonzero__(self):
for value in self:
if bool(value):
return True
return False
def apply_to_components(self, function):
self.x = function(self.x)
self.y = function(self.y)
@ -67,3 +95,37 @@ class Vector(list):
def place_at_origin(self):
self.x = 0
self.y = 0
class EVector(Vector):
def __init__(self, x=0, y=0, dx=0, dy=0, magnitude=None, angle=0):
Vector.__init__(self, x, y)
self.set_step(dx, dy, magnitude, angle)
def set_step(self, dx=0, dy=0, magnitude=None, angle=0):
"""specify angle in radians, counter-clockwise, 0 is up"""
if magnitude is not None:
self.magnitude = magnitude
self.angle = angle
self.dx, self.dy = get_delta(angle, magnitude, False)
else:
self.dx = dx
self.dy = dy
if dx == 0 and dy == 0:
self.magnitude = 0
self.angle = 0
else:
end = self.x + dx, self.y + dy
self.magnitude = get_distance(self, end)
self.angle = -get_angle(self, end) - pi
def __repr__(self):
return "<dx=%.2f, dy=%.2f, m=%.2f, ang=%.2f>" % \
(self.dx, self.dy, self.magnitude, self.angle)
def __nonzero__(self):
return bool(self.magnitude)
def move(self):
self += self.dx, self.dy

View File

@ -1,6 +1,10 @@
from random import randint, random
from math import sin, cos, atan2, radians, sqrt, pi
from pygame import Surface, PixelArray, Color
from pygame.mixer import get_num_channels, Channel
from pygame.locals import *
def get_step(start, end, speed):
x0, y0 = start
x1, y1 = end
@ -13,14 +17,15 @@ def get_angle(start, end, transpose=False):
angle = -angle - pi
return angle
def get_endpoint(start, angle, magnitude):
def get_endpoint(start, angle, magnitude, translate_angle=True):
"""clockwise, 0 is up"""
x0, y0 = start
dx, dy = get_delta(angle, magnitude)
dx, dy = get_delta(angle, magnitude, translate_angle)
return x0 + dx, y0 + dy
def get_delta(angle, magnitude):
angle = radians(angle)
def get_delta(angle, magnitude, translate_angle=True):
if translate_angle:
angle = radians(angle)
return sin(angle) * magnitude, -cos(angle) * magnitude
def rotate_2d(point, center, angle, translate_angle=True):
@ -65,6 +70,7 @@ def place_in_rect(rect, incoming, contain=True, *args):
if not collides:
break
# from http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c
def get_intersection(p0, p1, p2, p3):
x0, y0 = p0
x1, y1 = p1
@ -112,3 +118,95 @@ def get_value_in_range(start, end, position, reverse=False):
if reverse:
position = 1 - position
return (end - start) * position + start
def render_box(font, text, antialias, color, background=None, border=None,
border_width=1, padding=0):
surface = font.render(text, antialias, color, background)
if padding:
if isinstance(padding, int):
padding = [padding] * 2
padding = [x * 2 for x in padding]
rect = surface.get_rect()
padded_surface = Surface(rect.inflate(padding).size, SRCALPHA)
if background is not None:
padded_surface.fill(background)
rect.center = padded_surface.get_rect().center
padded_surface.blit(surface, rect)
surface = padded_surface
if border is not None:
if isinstance(border_width, int):
border_width = [border_width] * 2
border_width = [x * 2 for x in border_width]
rect = surface.get_rect()
bordered_surface = Surface(rect.inflate(border_width).size)
bordered_surface.fill(border)
rect.center = bordered_surface.get_rect().center
bordered_surface.blit(surface, rect)
surface = bordered_surface
return surface
def get_color_swapped_surface(surface, current, replacement):
swapped = surface.copy()
pixels = PixelArray(swapped)
pixels.replace(current, replacement)
del pixels
return swapped
def get_busy_channel_count():
count = 0
for index in xrange(get_num_channels()):
count += Channel(index).get_busy()
return count
def get_hue_shifted_surface(base, offset):
surface = base.copy()
pixels = PixelArray(surface)
color = Color(0, 0, 0)
for x in xrange(surface.get_width()):
for y in xrange(surface.get_height()):
h, s, l, a = Color(*surface.unmap_rgb(pixels[x][y])).hsla
if a:
color.hsla = (h + offset) % 360, s, l, a
pixels[x][y] = color
del pixels
return surface
def get_inverted_surface(base):
surface = base.copy()
pixels = PixelArray(surface)
for x in xrange(surface.get_width()):
for y in xrange(surface.get_height()):
color = Color(*surface.unmap_rgb(pixels[x][y]))
if color.hsla[3]:
color.r = 255 - color.r
color.g = 255 - color.g
color.b = 255 - color.b
pixels[x][y] = color
del pixels
return surface
def fill_tile(surface, tile):
for x in xrange(0, surface.get_width(), tile.get_width()):
for y in xrange(0, surface.get_height(), tile.get_height()):
surface.blit(tile, (x, y))
def get_shadowed_text(text, font, offset, color, antialias=True, shadow_color=(0, 0, 0),
colorkey=(255, 0, 255)):
foreground = font.render(text, antialias, color)
background = font.render(text, antialias, shadow_color)
alpha = SRCALPHA if antialias else 0
surface = Surface((foreground.get_width() + abs(offset[0]),
foreground.get_height() + abs(offset[1])), alpha)
if not antialias:
surface.set_colorkey(colorkey)
surface.fill(colorkey)
surface.blit(background, ((abs(offset[0]) + offset[0]) / 2,
(abs(offset[1]) + offset[1]) / 2))
surface.blit(foreground, ((abs(offset[0]) - offset[0]) / 2,
(abs(offset[1]) - offset[1]) / 2))
return surface
def get_hsla_color(hue, saturation=100, lightness=50, alpha=100):
color = Color(0, 0, 0, 0)
color.hsla = hue % 360, saturation, lightness, alpha
return color

14
pgfw/gfx_extension.py Normal file
View File

@ -0,0 +1,14 @@
from pygame.gfxdraw import (aacircle, filled_circle, aatrigon, filled_trigon,
aapolygon, filled_polygon)
def aa_filled_circle(surface, cx, cy, radius, color):
aacircle(surface, cx, cy, radius, color)
filled_circle(surface, cx, cy, radius, color)
def aa_filled_trigon(surface, x1, y1, x2, y2, x3, y3, color):
aatrigon(surface, x1, y1, x2, y2, x3, y3, color)
filled_trigon(surface, x1, y1, x2, y2, x3, y3, color)
def aa_filled_polygon(surface, points, color):
aapolygon(surface, points, color)
filled_polygon(surface, points, color)