June 7, 2018♦
from os import listdir
from os.path import join
from pygame.mixer import Channel, Sound, music, find_channel
from GameChild import *
from Input import *
class Audio(GameChild):
current_channel = None
paused = False
muted = False
def __init__(self, game):
GameChild.__init__(self, game)
self.delegate = self.get_delegate()
self.load_fx()
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 respond(self, event):
if self.delegate.compare(event, "mute"):
self.mute()
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()
from random import randint, random, choice
from math import sin, tan, radians, copysign, degrees, cos, asin
from os.path import join
from collections import deque
from itertools import chain
from pygame.locals import *
from pygame import Surface, Color, PixelArray
from pygame.font import Font
from pygame.mixer import Sound, Channel, get_num_channels
from pygame.draw import polygon, line, circle, aaline
from pygame.gfxdraw import aapolygon, aacircle, filled_circle
from pygame.image import load, save
from pygame.transform import rotate, smoothscale, rotozoom, scale
from pygame.event import clear
from lib.pgfw.pgfw.Game import Game
from lib.pgfw.pgfw.GameChild import GameChild
from lib.pgfw.pgfw.Sprite import Sprite
from lib.pgfw.pgfw.Animation import Animation
from lib.pgfw.pgfw.extension import get_distance, get_delta
class SoundEffect(GameChild, Sound):
def __init__(self, parent, path, volume=1.0):
GameChild.__init__(self, parent)
Sound.__init__(self, path)
self.display_surface = self.get_display_surface()
self.set_volume(volume)
def play(self, loops=0, maxtime=0, fade_ms=0, position=None, x=None):
channel = Sound.play(self, loops, maxtime, fade_ms)
if x is not None:
position = float(x) / self.display_surface.get_width()
if position is not None and channel is not None:
channel.set_volume(*self.get_panning(position))
return channel
def get_panning(self, position):
return 1 - max(0, ((position - .5) * 2)), \
1 + min(0, ((position - .5) * 2))
class Vector(GameChild):
def __init__(self, parent, x, y, z):
GameChild.__init__(self, parent)
self.x, self.y, self.z = x, y, z
def __repr__(self):
return "<%.3f %.3f %.3f>" % (self.x, self.y, self.z)
def rotate(self, th, x=0, y=0, z=0):
k = x, y, z
v = self.x, self.y, self.z
c = cos(th)
vs = [cc * c for cc in v]
s = sin(th)
vc = [(k[1] * v[2] - k[2] * v[1]) * s,
(k[2] * v[0] - k[0] * v[2]) * s,
(k[0] * v[1] - k[1] * v[0]) * s]
dp = sum(k[ii] * v[ii] for ii in xrange(len(k)))
kd = [cc * dp * (1 - c) for cc in k]
self.x, self.y, self.z = (vs[ii] + vc[ii] + kd[ii] for ii in \
xrange(len(v)))
def get_screen_coordinates(self, radius, center=None):
if center is None:
center = self.display_surface.get_rect().center
cx, cy = center
x = int(round(cx + self.x * radius))
y = int(round(cy + self.y * radius))
return x, y
class Wisp(Game, Animation):
LEVEL_DATA = (((213, 255, 34, 200), (134, 91, 42, 128),
(220, 255), (190, 240), (190, 240),
(192, 87, 30), (0, 180, 0),
(((-3, -3, 2), (3, 1, -2), (0, 0, 4), (100, 100, 200), (30, (450, 200))),
((-3, -3, 2), (0, 0, 4), (-1, -5, 0), (80, 100, 155), (30, (450, 200))),
((-1, 5, 0), (0, 0, 4), (1, 1, -2), (122, 68, 72), (30, (450, 200))),
((1, 1, -2), (-1, 5, 0), (-2, 4, 3), (180, 60, 180), (20, (450, 300)))), 1),
((235, 24, 82, 130), (86, 208, 64, 200),
(180, 210), (190, 240), (190, 240),
(213, 255, 34), (240, 20, 200),
(((5, 2, -1), (1, -3, 2), (-3, 5, 3), (222, 122, 0), (5, (450, 210))),
((1, -3, 2), (-3, 5, 3), (4, 1, 8), (30, 153, 87), (5, (450, 210))),
((-3, 5, 3), (4, 1, 8), (2, -3, -3), (200, 130, 10), (5, (450, 210))),
((2, 4, 2), (8, 11, -4), (-14, -4, 3), (30, 180, 90), (11, (450, 290)))), 2),
((100, 100, 255, 160), (82, 64, 123, 240),
(220, 250), (220, 250), (120, 180),
(0, 255, 255), (50, 40, 30),
(((2, 1, 2), (4, -2, -2), (-5, 3, -2), (200, 140, 201), (35, (450, 190))),
((2, 1, 2), (-5, 3, -2), (-1, 6, 1), (30, 87, 153), (35, (450, 190))),
((-5, 3, -2), (-1, 0, 2), (3, 5, -1), (30, 87, 153), (20, (450, 300))),
((-1, 0, 2), (3, 5, -1), (1, 10, -3), (255, 142, 193), (20, (450, 300)))), 3),
((42, 91, 134, 200), (255, 34, 213, 128),
(200, 255), (190, 200), (200, 255),
(78, 98, 28), (213, 255, 34),
(((-3, 4, 1), (0, 1, 0), (-2, -2, -1), (200, 200, 100), (22, (450, 200))),
((-3, 4, 1), (0, 1, 0), (1, 5, 3), (140, 140, 80), (22, (450, 200))),
((-2, -2, -1), (1, 5, 3), (-1, -1, -3), (85, 85, 85), (22, (450, 200))),
((0, -3, 2), (3, 4, -2), (-2, -1, -1), (202, 101, 90), (13, (450, 240))),
((3, 4, -2), (-2, -1, -1), (2, 0, 1), (140, 140, 80), (13, (450, 280)))), 4),
((100, 255, 100, 80), (64, 143, 82, 170),
(210, 240), (240, 255), (180, 200),
(60, 60, 120), (200, 120, 120),
(((-2, 5, 3), (3, 1, -3), (4, -2, 1), (48, 84, 148), (30, (450, 180))),
((3, 1, -3), (4, -2, 1), (1, 1, 0), (32, 200, 200), (30, (450, 180))),
((1, 1, 0), (4, -2, 1), (-3, -2, -1), (48, 148, 84), (30, (450, 180)),
(-3, -4, 1), (3, -4, 1), (0, 4, 2), (200, 30, 120), (25, (450, 300))),
((0, 4, 2), (3, -4, 1), (2, 0, -4), (48, 148, 84), (25, (450, 300)))), 5),
((32, 157, 85, 230), (200, 100, 100, 170),
(180, 230), (180, 200), (240, 255),
(80, 80, 80), (255, 255, 255),
(((3, 2, 1), (1, 2, 3), (-2, -3, -1), (52, 103, 183), (22, (430, 160))),
((1, 2, 3), (-2, -3, -1), (-5, -3, 4), (61, 133, 172), (22, (430, 160))),
((-5, -3, 4), (-2, -3, -1), (2, 1, 0), (88, 109, 255), (22, (430, 160))),
((3, 2, 1), (2, 1, 0), (-5, -3, 4), (92, 255, 0), (22, (430, 160))),
((5, 8, 2), (3, -3, -5), (-2, -1, 3), (222, 222, 102), (22, (430, 240))),
((2, 1, 5), (10, 1, 7), (-3, -4, -2), (30, 30, 180), (22, (430, 240)))), 6))
FORM_ROTATION_SPEED = .01
FORM_ROTATION_LIMIT = .25
IN_GAME_MUSIC_FADE_STEP = .01
def __init__(self):
Game.__init__(self)
Animation.__init__(self, self)
self.lives = Lives(self)
self.prompts = Prompts(self)
self.platform = Platform(self)
self.message = Message(self)
self.drop = Drop(self)
self.bird = Bird(self)
self.best = Best(self)
self.title_music = Sound(self.get_resource("LLll75E.ogg"))
self.title_music.set_volume(.75)
self.in_game_music = Sound(self.get_resource("BOGSWEATer.ogg"))
self.end_music = Sound(self.get_resource("Hey_Who_is_This.ogg"))
self.end_music.set_volume(.5)
self.set_levels()
self.subscribe(self.respond)
self.register(self.unsuppress_commands, self.fade_out_in_game_music)
self.reset()
clear()
def print_channel_contents(self):
print "channels\n----------"
for index in xrange(get_num_channels()):
channel = Channel(index)
sound = channel.get_sound()
print channel.get_busy(), sound, sound.get_length() if sound else None
print
def set_levels(self):
levels = self.levels = []
for ii, parameters in enumerate(self.LEVEL_DATA):
levels.append(Level(self, ii, parameters[:2], parameters[2:5], parameters[5:7], parameters[7], parameters[8]))
def respond(self, event):
if not self.suppressing_commands:
if self.delegate.compare(event, "reset-game"):
self.reset()
elif self.delegate.compare(event, "any") and self.waiting_to_start:
self.message.set_text(" ")
self.in_game_music_channel = self.in_game_music.play(-1, 0, 3000)
self.title_music.fadeout(3000)
self.waiting_to_start = False
self.in_game = True
self.prompts.show()
self.drop.begin_level()
elif self.delegate.compare(event, "any") and self.game_complete:
self.reset()
self.game_complete = False
def unsuppress_commands(self):
self.suppressing_commands = False
def fade_out_in_game_music(self):
pass
def reset(self):
self.set_forms()
self.lives.reset()
self.level_index = 0
self.form_rotation = 0
self.cumulative_time = 0
self.form_rotation_direction = choice((-1, 1))
self.prompts.reset()
self.platform.reset()
self.bird.reset()
for level in self.levels:
level.ufo.reset()
self.message.unhighlight()
self.message.set_text("press key")
self.title_music.fadeout(3000)
if self.in_game_music.get_num_channels() or self.end_music.get_num_channels():
self.title_music.play(-1, 0, 3000)
else:
self.title_music.play(-1)
self.game_complete = False
self.suppressing_commands = True
self.play(self.unsuppress_commands, delay=1000, play_once=True)
self.waiting_to_start = True
self.in_game = False
self.in_game_music.fadeout(3000)
self.end_music.fadeout(3000)
self.drop.reset()
def set_forms(self):
for level in self.levels:
level.set_form()
def get_level(self, offset=0):
return self.levels[self.level_index + offset]
def is_first_level(self):
return self.level_index == 0
def advance_level(self, forward=True):
self.level_index += (-1, 1)[forward]
if self.level_index < 0:
self.level_index = 0
self.form_rotation = 0
if self.level_index == len(self.LEVEL_DATA):
self.level_index = len(self.LEVEL_DATA) - 1
self.game_complete = True
self.message.highlight()
self.message.set_text("%.1f" % (self.cumulative_time / 1000.0))
self.suppressing_commands = True
self.play(self.unsuppress_commands, delay=1000, play_once=True)
self.in_game_music.fadeout(3000)
self.end_music.play(-1, 0, 3000)
self.drop.active = False
self.best.add(self.cumulative_time)
self.bird.fly()
else:
if forward:
self.bird.add()
self.prompts.zone.hide()
self.drop.begin_level()
self.set_forms()
def update(self):
Animation.update(self)
self.get_screen().fill([randint(*limits) for limits in self.get_level().background])
self.bird.laser()
if not self.game_complete:
self.drop.update()
self.prompts.update()
self.platform.update()
self.platform.update_far_trees()
if not self.game_complete:
for shape in self.get_level().form:
points = []
for vector in shape[:3]:
vector.rotate(self.form_rotation_direction * self.FORM_ROTATION_SPEED, x=1)
points.append(vector.get_screen_coordinates(*shape[4]))
aapolygon(self.get_screen(), points, shape[3])
polygon(self.get_screen(), shape[3], points)
self.form_rotation += self.form_rotation_direction * self.FORM_ROTATION_SPEED
if self.form_rotation < -self.FORM_ROTATION_LIMIT:
self.form_rotation += self.FORM_ROTATION_SPEED * 2
self.form_rotation_direction = -self.form_rotation_direction
elif self.form_rotation > self.FORM_ROTATION_LIMIT:
self.form_rotation -= self.FORM_ROTATION_SPEED * 2
self.form_rotation_direction = -self.form_rotation_direction
self.platform.update_near_trees()
self.lives.update()
self.message.update()
self.best.update()
self.bird.update()
class Lives(Sprite):
INITIAL_COUNT = 3
INTERVAL = 4000, 200
LOCATION = 10, 10
MARGIN = 8
def __init__(self, parent):
Sprite.__init__(self, parent, self.INTERVAL)
self.load_from_path(self.get_resource("Sumotree"), True, False)
self.location.topleft = self.LOCATION
self.add_location(offset=(self.location.w + self.MARGIN, 0), count=self.INITIAL_COUNT - 1)
def reset(self):
self.count = self.INITIAL_COUNT
self.fade(0, False)
def remove(self):
self.count -= 1
self.fade(2000, True, self.count)
def are_extinguished(self):
return self.count == 0
class Prompts(GameChild):
def __init__(self, parent):
GameChild.__init__(self, parent)
self.arrows = Arrows(self)
self.zone = Zone(self)
def reset(self):
self.arrows.hide()
self.zone.hide()
def show(self):
self.arrows.unhide()
self.zone.unhide()
self.arrows.finger.get_current_frameset().reset()
def update(self):
drop, level = self.get_game().drop, self.get_game().get_level()
if not self.get_game().waiting_to_start and drop.fall_speed <= level.fall_speed * .1:
self.arrows.hide()
self.arrows.update()
self.zone.update()
class Arrows(Sprite):
MARGIN = 2
COUNT = 5
OFFSET = 9
def __init__(self, parent):
Sprite.__init__(self, parent)
self.set_frames()
self.finger = Finger(self)
self.location.centerx = Drop.X
def set_frames(self):
tick = load(self.get_resource("Scar.png")).convert()
transparent_color = (255, 0, 255)
tick.set_colorkey(transparent_color)
rect = tick.get_rect()
for oy in xrange(0, -rect.h - self.MARGIN - 1, -1):
step = rect.h + self.MARGIN
frame = Surface((rect.w, step * self.COUNT))
frame.fill(transparent_color)
frame.set_colorkey(transparent_color)
for y in xrange(oy, step * (self.COUNT + 1), step):
frame.blit(tick, (0, y))
self.add_frame(frame)
def hide(self):
Sprite.hide(self)
self.finger.hide()
def unhide(self):
Sprite.unhide(self)
self.finger.unhide()
def update(self):
if not self.is_hidden():
self.location.top = self.OFFSET + self.get_game().drop.get_ledge()
Sprite.update(self)
self.finger.update()
class Finger(Sprite):
INTERVAL = 500, 1200
OFFSET = 15, 6
def __init__(self, parent):
Sprite.__init__(self, parent, self.INTERVAL)
self.load_from_path(self.get_resource("reduce"), True, False)
def update(self):
if not self.is_hidden():
self.location.topleft = self.parent.location.move(*self.OFFSET).topleft
Sprite.update(self)
class Zone(Sprite):
POSITION = 158, 366
def __init__(self, parent):
Sprite.__init__(self, parent)
self.load_from_path(self.get_resource("Flab-skud.png"), True, False)
self.location.topleft = self.POSITION
class Message(Sprite):
def __init__(self, parent):
Sprite.__init__(self, parent)
def set_text(self, text):
self.text = text
self.clear_frames()
color, shadow_color = Color(0, 0, 0), Color(0, 0, 0)
spaced = text[0]
for char in text[1:]:
spaced += " " + char
for hue in xrange(0, 360, 10):
color.hsla = hue, 100, 40, 100
font = Font(self.get_resource("AlegreyaSansSC-BlackItalic.ttf"), (32, 68)[self.highlit])
foreground = font.render(spaced, True, color)
self.add_frame(foreground)
ds = self.get_display_surface().get_rect()
if self.highlit:
self.location.center = ds.center
else:
self.location.center = ds.centerx, ds.h - 25
def clear_text(self):
self.text = ""
self.clear_frames()
def highlight(self):
self.highlit = True
def unhighlight(self):
self.highlit = False
class Level(GameChild):
def __init__(self, parent, index, platform_colors, background, tree_colors, form_coordinates, fall_speed):
GameChild.__init__(self, parent)
self.index = index
self.platform_colors = platform_colors
self.background = background
self.tree_colors = tree_colors
self.form_coordinates = form_coordinates
self.fall_speed = fall_speed
self.womb = Womb(self)
self.set_form()
self.ufo = UFO(self)
def set_form(self):
form = self.form = []
for shape in self.form_coordinates:
form.append([Vector(self, *coordinates) for coordinates in shape[:3]] + list(shape[3:]))
class Womb(Sprite):
MAX_AMPLITUDES = 60, 60, 30, 40, 30, 45
PERIODS = .05, .15, .1, .25, .05, 3
INTERVAL = 0
AMPLITUDE_STEP = 1
def __init__(self, parent):
Sprite.__init__(self, parent, self.INTERVAL)
self.set_frames()
def set_frames(self):
index = self.parent.index
base = load(self.get_resource(join("u_IO_C", "%i.png" % index))).convert_alpha()
w, h = base.get_size()
max_a, step = self.MAX_AMPLITUDES[index], self.AMPLITUDE_STEP
for amplitude in chain(xrange(0, max_a, step), xrange(max_a, -max_a, -step), xrange(-max_a, 0, step)):
frame = Surface((w * 2, h), SRCALPHA)
pixels = PixelArray(base)
for y in xrange(len(pixels[0])):
offset = sin(y * self.PERIODS[index]) * amplitude + w / 2
for x in xrange(len(pixels)):
frame.set_at((int(round(x + offset)), y), pixels[x][y])
self.add_frame(frame)
class UFO(Sprite):
INTERVAL = 300
TAKE_OFF_SPEED = -.3
TAKE_OFF_HEIGHT = 20
TAKE_OFF_DELAY = 800
ENTER_SHIFT = 2
FLY_SPEED = 4, -3
FLY_DELAY = 800
def __init__(self, parent):
Sprite.__init__(self, parent, self.INTERVAL)
self.load_from_path(self.get_resource(join("Foil_of_aluminum", str(parent.index))), True)
self.y_steps = self.get_game().platform.get_y_steps()
self.take_off_sound = SoundEffect(self, self.get_resource("monaural.wav"))
self.fly_sound = SoundEffect(self, self.get_resource("Noise-Police.wav"))
self.register(self.take_off, self.fly)
def reset(self):
self.yi = 0
self.entering = False
self.taking_off = False
self.flying = False
self.location.centerx = self.get_game().drop.man.location.w + Man.WALK[1]
self.halt(self.take_off)
self.halt(self.fly)
self.hide()
def enter(self):
self.unhide()
self.entering = True
def get_y(self):
return self.y_steps[self.yi]
def queue_take_off(self):
self.play(self.take_off, delay=self.TAKE_OFF_DELAY, play_once=True)
def take_off(self):
self.taking_off = True
self.take_off_sound.play()
def queue_fly(self):
self.play(self.fly, delay=self.FLY_DELAY, play_once=True)
self.take_off_sound.fadeout(500)
def fly(self):
self.flying = True
self.fly_sound.play()
def update(self):
substitute = None
if self.entering:
rect = self.get_game().platform.rect
self.location.bottom = rect.top + self.get_y() + self.ENTER_SHIFT
if self.yi >= len(self.y_steps) - 1:
self.get_game().platform.halt()
self.entering = False
else:
self.yi += 1
y_scale = 3 * float(self.get_y()) / rect.h
w, h = self.location.size
substitute = scale(self.get_current_frame(), (int(w + w * y_scale),
int(h + h * y_scale)))
elif self.taking_off:
self.move(dy=self.TAKE_OFF_SPEED)
if self.location.bottom < self.get_game().platform.rect.top - self.TAKE_OFF_HEIGHT:
self.taking_off = False
self.queue_fly()
elif self.flying:
self.move(*self.FLY_SPEED)
if self.location.left > self.get_display_surface().get_width():
self.flying = False
Sprite.update(self, substitute=substitute)
class Platform(Animation):
ANGLE = 30
MARGIN = 50, 50
LENGTH = 50
BUFFER = 4
CELL_COUNT = 10
def __init__(self, parent):
Animation.__init__(self, parent, self.move)
length = self.LENGTH
dx = length / tan(radians(self.ANGLE))
inset, offset = self.MARGIN
ds = self.get_display_surface().get_rect()
surface = self.surface = Surface((ds.w - inset * 2, length), SRCALPHA)
rect = self.rect = surface.get_rect()
rect.bottomleft = inset, ds.h - offset
self.corners = (0, rect.h), (rect.w, rect.h), (rect.w - dx, 0), (dx, 0)
self.dy_nodeset = self.get_game().interpolator.get_nodeset("platform-dy")
def move(self):
self.y_offset -= .5
if self.y_offset < -self.BUFFER:
self.y_offset = 0
self.y_switch = not self.y_switch
def reset(self):
self.y_offset = 0
self.y_switch = False
self.trees = []
self.clear_trees_to_remove()
self.play()
def clear_trees_to_remove(self):
self.trees_to_remove = []
def get_y_steps(self):
y_steps = []
nodeset = self.get_game().interpolator.get_nodeset("tree-y-steps")
for ii in xrange(Tree.STEP_COUNT):
y_steps.append(int(self.rect.h * nodeset.get_y(float(ii) / Tree.STEP_COUNT)))
return y_steps
def queue_tree_removal(self, tree):
self.trees_to_remove.append(tree)
def update(self):
if self.is_playing() and random() < .08:
self.trees.append(Tree(self))
Animation.update(self)
background, foreground = self.get_game().get_level().platform_colors
surface = self.surface.copy()
aapolygon(surface, self.corners, background)
polygon(surface, background, self.corners)
buf = self.BUFFER
y = self.y_offset
ys = self.y_switch
rect = self.rect
dx = self.corners[-1][0]
cx = rect.centerx
while y < rect.h:
dy = int(self.dy_nodeset.get_y(float(y + buf) / (rect.h + buf)) * (rect.h + buf))
n = ys
while True:
x0 = self.get_x(n)
n += 1
x1 = self.get_x(n)
n += 1
if x1 > rect.w - dx:
break
x0_a = self.get_angle(x0)
x1_a = self.get_angle(x1)
points = (x0 + 1 + copysign(y / tan(x0_a), x0 - cx), y), \
(x1 - 1 + copysign(y / tan(x1_a), x1 - cx), y), \
(x1 - 1 + copysign((y + dy) / tan(x1_a), x1 - cx), y + dy), \
(x0 + 1 + copysign((y + dy) / tan(x0_a), x0 - cx), y + dy)
aapolygon(surface, points, foreground)
polygon(surface, foreground, points)
ys = not ys
y += dy
self.get_display_surface().blit(surface, rect)
def get_x(self, n):
dx = self.corners[-1][0]
return int(dx + n * ((self.rect.w - dx * 2) / float(self.CELL_COUNT)))
def get_angle(self, x):
return radians(self.ANGLE) * abs(x - self.rect.centerx) / abs(self.corners[-1][0] - self.rect.centerx)
def update_far_trees(self):
self.ufo_drawn = False
half = self.rect.h / 2
for tree in self.trees:
y = tree.get_y()
if y <= half:
self.draw_ufo(y)
tree.update()
self.draw_ufo(half)
def draw_ufo(self, y):
if not self.ufo_drawn:
ufo = self.get_game().get_level().ufo
if ufo.get_y() < y:
ufo.update()
self.ufo_drawn = True
def update_near_trees(self):
half = self.rect.h / 2
for tree in self.trees:
y = tree.get_y()
if y > half:
self.draw_ufo(y)
tree.update()
self.draw_ufo(self.rect.h + 1)
for tree in self.trees_to_remove:
self.trees.remove(tree)
self.clear_trees_to_remove()
class Tree(Sprite):
MAX_TRUNK_HEIGHT = 8
MAX_LEAVES_RADIUS = 38
MIN_X_SCALE = .4
STEP_COUNT = 75
def __init__(self, parent):
Sprite.__init__(self, parent)
self.xr = random()
self.y_steps = parent.get_y_steps()
self.yi = 0
def get_y(self):
return self.y_steps[self.yi]
def update(self):
rect = self.parent.rect
trunk_c, leaves_c = self.get_game().get_level().tree_colors
y = self.get_y()
x_scale = (self.MIN_X_SCALE + (1 - self.MIN_X_SCALE) * (1 - abs(self.xr - .5) * 2))
y_scale = float(y) / rect.h
height = int(self.MAX_TRUNK_HEIGHT * x_scale * y_scale)
x_in = int((rect.h - y) / tan(radians(self.parent.ANGLE)))
x = int((rect.w - x_in * 2) * self.xr + x_in)
radius = int(self.MAX_LEAVES_RADIUS * x_scale * y_scale)
if radius > 4:
start = rect.left + x, rect.top + y - 6
end = rect.left + x, rect.top + y - height - 6
ds = self.get_display_surface()
line(ds, trunk_c, start, (end[0], end[1] - 2), max(3, int(round(7 * x_scale * y_scale))))
aacircle(ds, end[0], end[1] - radius, radius, leaves_c)
filled_circle(ds, end[0], end[1] - radius, radius, leaves_c)
Sprite.update(self)
if self.parent.is_playing():
self.yi += 1
else:
self.parent.queue_tree_removal(self)
class Drop(Animation):
BLINK_INTERVAL = 40
X = 210
START_Y = 100
END_MARGIN = 38
DELAY = 4000
ACCELERATION = .1
SAFE_ZONE = 15
def __init__(self, parent):
Animation.__init__(self, parent)
self.limit = self.get_game().platform.rect.top
self.success_sound = Sound(self.get_resource("man-before-time.ogg"))
self.success_sound.set_volume(.9)
self.miss_sound = Sound(self.get_resource("lvjh.wav"))
self.miss_sound.set_volume(.32)
self.scale_nodeset = self.get_game().interpolator.get_nodeset("scale")
self.deceleration_nodeset = self.get_game().interpolator.get_nodeset("deceleration")
self.sun = Sun(self)
self.man = Man(self)
self.subscribe(self.respond)
self.register(self.blink, interval=self.BLINK_INTERVAL)
self.register(self.unsuppress_commands, self.drop_bomb)
def respond(self, event):
if self.active:
if not self.suppressing_commands:
compare = self.get_game().delegate.compare
if not self.succeeded and not self.failed:
if self.falling and compare(event, "any") and not self.decelerating:
self.decelerating = True
elif compare(event, "any", True) and self.decelerating:
self.decelerating = False
self.deceleration_elapsed = 0
self.fall_speed = self.get_game().get_level().fall_speed
else:
self.man.reset()
self.get_game().platform.play()
if self.succeeded:
self.get_game().advance_level()
elif self.failed:
if self.get_game().lives.are_extinguished():
self.get_game().reset()
else:
self.get_game().advance_level(False)
def unsuppress_commands(self):
self.suppressing_commands = False
def blink(self):
self.bomb_visible = not self.bomb_visible
def reset(self):
self.active = False
self.bomb_visible = True
self.suppressing_commands = False
self.decelerating = False
self.waiting_for_end_level_confirm = False
self.bottom = None
self.deceleration_elapsed = 0
self.halt()
self.man.reset()
def begin_level(self):
self.active = True
self.failed = False
self.succeeded = False
self.decelerating = False
self.waiting_for_end_level_confirm = False
level = self.get_game().get_level()
if self.get_game().level_index > 0 or self.get_game().message.text == "miss":
self.get_game().message.clear_text()
self.fall_speed = level.fall_speed
self.fall_elapsed = 0
self.suppressing_commands = False
self.falling = False
self.neutralized = False
self.traveled = 0
self.delay_elapsed = 0
self.sun.reset()
# self.play(self.blink)
self.play(self.drop_bomb, delay=self.DELAY, play_once=True)
def drop_bomb(self):
# self.halt(self.blink)
self.bomb_visible = True
self.falling = True
self.sun.place_at_zenith()
self.fall_speed = self.get_game().get_level().fall_speed
def update(self):
self.bottom = None
if self.active:
Animation.update(self)
if not self.falling and not self.waiting_for_end_level_confirm:
self.delay_elapsed += self.get_game().time_filter.get_last_frame_duration()
self.sun.offset()
self.sun.update()
self.man.update()
ds = self.get_display_surface()
# line(ds, (255, 0, 0), (self.X - 10, self.limit), (self.X + 10, self.limit))
base = self.get_game().get_level().womb.get_current_frame()
if not self.falling and not self.failed and not self.succeeded:
if self.bomb_visible:
self.draw_cap()
elif self.falling:
last_frame_duration = self.get_game().time_filter.get_last_frame_duration()
self.fall_elapsed += last_frame_duration
if not self.decelerating:
self.fall_speed += self.fall_speed * self.ACCELERATION
else:
self.fall_speed -= self.deceleration_nodeset.get_y(self.deceleration_elapsed)
if self.fall_speed < 0:
self.fall_speed = 0
self.deceleration_elapsed += last_frame_duration
self.traveled += self.fall_speed
if not self.is_playing(self.blink):
for y in xrange(int(self.traveled), -1, -1):
if y == 0:
self.draw_cap()
else:
scale = .3 + .7 * (1 - y / float(self.limit - self.START_Y))
surface = smoothscale(base, (int(base.get_width() * scale), int(base.get_height() * scale)))
rect = surface.get_rect()
rect.center = self.X, self.START_Y + y
bottom = rect.bottom
ds.blit(surface, rect)
if y == int(self.traveled):
self.bottom = bottom
if not self.failed and not self.succeeded:
if bottom > self.limit:
self.failed = True
self.falling = False
if not self.fall_speed and self.is_in_safe_zone():
self.succeeded = True
self.falling = False
self.success_sound.play()
if not self.waiting_for_end_level_confirm:
if self.failed:
lives = self.get_game().lives
lives.remove()
if self.get_game().level_index > 0:
self.get_game().bird.off()
self.get_game().get_level(-1).ufo.reset()
if lives.are_extinguished():
text = "game over"
else:
text = "miss"
self.miss_sound.play()
self.get_game().message.set_text(text)
self.suppressing_commands = True
self.play(self.unsuppress_commands, delay=1000, play_once=True)
self.waiting_for_end_level_confirm = True
elif self.succeeded:
self.get_game().message.set_text("%.1f" % (self.fall_elapsed / 1000.0))
self.get_game().cumulative_time += self.fall_elapsed
self.suppressing_commands = True
self.play(self.unsuppress_commands, delay=1000, play_once=True)
self.man.emerge()
self.waiting_for_end_level_confirm = True
self.get_game().get_level().ufo.enter()
def draw_cap(self):
cap = self.get_game().get_level().womb
cap.location.center = self.X, self.START_Y
cap.update()
def is_in_safe_zone(self):
if self.bottom:
return 0 <= self.limit - self.bottom <= self.SAFE_ZONE
def get_ledge(self):
return self.START_Y + self.get_game().get_level().womb.location.h / 2 + self.traveled
class Sun(Sprite):
INTERVAL = 100
FRAME_COUNT = 8
MIN_Y = 45
def __init__(self, parent):
Sprite.__init__(self, parent, self.INTERVAL)
self.set_frames()
def set_frames(self):
base = load(self.get_resource("PP-eleur.png")).convert_alpha()
self.add_frame(base)
hue_step = 360 / self.FRAME_COUNT
color = Color(0, 0, 0)
for hue_offset in xrange(hue_step, 360, hue_step):
frame = base.copy()
pixels = PixelArray(frame)
for x in xrange(len(pixels)):
for y in xrange(len(pixels[0])):
h, s, l, a = Color(*frame.unmap_rgb(pixels[x][y])).hsla
if a:
color.hsla = (h + hue_offset) % 360, s, l, a
pixels[x][y] = color
del pixels
self.add_frame(frame)
def reset(self):
self.location.center = self.parent.X, self.parent.START_Y
def offset(self):
path_length = self.parent.START_Y - self.MIN_Y
offset = path_length * float(self.parent.delay_elapsed) / self.parent.DELAY
self.location.centery = max(self.MIN_Y, self.parent.START_Y - offset)
def place_at_zenith(self):
self.location.centery = self.MIN_Y
class Man(Sprite):
INTERVAL = 300
SPEED = .8
Y = 265
WALK = 200, 248
CLIMB_IN_DELAY = 1800
BOUNCE = 2
def __init__(self, parent):
Sprite.__init__(self, parent, self.INTERVAL)
self.climb_in_sound = SoundEffect(self, self.get_resource("ghost__Writer.wav"), .8)
self.set_frames()
self.register(self.climb_in)
def set_frames(self):
base = load(self.get_resource("no-mouth.png")).convert_alpha()
self.add_frame(base)
frame = Surface(base.get_size(), SRCALPHA)
frame.blit(base, (0, self.BOUNCE))
self.add_frame(frame)
def reset(self):
self.hide()
self.stop()
self.halt()
def emerge(self):
self.unhide()
self.play()
self.location.topleft = self.WALK[0], self.Y
self.go(self.SPEED)
def climb_in(self):
self.hide()
self.get_game().get_level().ufo.queue_take_off()
self.climb_in_sound.play()
def update(self):
if self.is_going() and self.location.left >= self.WALK[1]:
self.stop()
self.halt()
self.play(self.climb_in, delay=self.CLIMB_IN_DELAY, play_once=True)
Sprite.update(self)
class Bird(GameChild):
# INTERVAL = 200, 200, 200, 200, 200, 100, 200, 200, 100
INTERVAL = 1000, 100, 100, 100, 100, 100, 100, 300, 100
LOCATION = 122, 351
MARGIN = 30, 15
SPEED = 3
TRAIL_LENGTH = 4
BEAK_OFFSET = 4
LASER_DASH_LENGTH = 5
LASER_HUE_STEP = 30
LASER_HUE_SHIFT = 8
def __init__(self, parent):
GameChild.__init__(self, parent)
def reset(self):
self.birds = []
self.add()
self.flying = False
colors = self.trail_colors = deque()
for hue in xrange(0, 360, 360 / self.TRAIL_LENGTH):
color = Color(0, 0, 0)
color.hsla = hue, 100, 50, 100
colors.append(color)
self.laser_base_hue = 0
def add(self):
mx, my = self.MARGIN
count = self.get_game().level_index
bird = Sprite(self)
self.set_frames(bird)
if count == 0:
ox, oy = 0, 0
bird.location.left = self.LOCATION[0]
bird.location.bottom = self.get_game().platform.rect.top - Drop.SAFE_ZONE + self.BEAK_OFFSET
else:
if count < 3:
ox, oy = count * -mx, 0
elif count < 5:
ox, oy = (count - 3) * -mx - mx / 2, -my
else:
ox, oy = -mx, -my * 2
bird.location.topleft = self.birds[0].location.move(ox, oy).topleft
bird.trail = deque(maxlen=self.TRAIL_LENGTH)
bird.offset = None
bird.halt()
self.birds.append(bird)
def off(self):
self.birds[-1].fade(2000, True, -1)
def set_frames(self, bird):
bird.load_from_path(self.get_resource("Gray-Fleshe"), True, query="f[0-4].png")
bird.add_frameset([0, 1, 2, 3, 4, 3, 2, 1, 0], self.INTERVAL, "fly", True)
def laser(self):
drop = self.get_game().drop
if drop.is_in_safe_zone():
self.laser_base_hue = (self.laser_base_hue + self.LASER_HUE_SHIFT) % 360
bird = self.birds[0]
base = bird.location
bottom = base.bottom - self.BEAK_OFFSET + 1
if bird.offset:
bottom -= bird.offset[1]
line(self.get_display_surface(), (255, 0, 0), (base.centerx, bottom), (Drop.X, drop.bottom))
def fly(self):
self.flying = True
for bird in self.birds:
self.set_course(bird)
def set_course(self, bird):
bird.course = Course(randint(0, 360), self.SPEED)
def update(self):
for bird in self.birds:
if not bird.location.fader.get_alpha():
self.birds.remove(bird)
if self.flying:
for bird in self.birds:
if random() < .01:
self.set_course(bird)
limit = self.get_display_surface().get_rect()
location = bird.location
if location.left > limit.right:
bird.move(-limit.w, 0)
if location.top > limit.bottom:
bird.move(0, -limit.h)
if location.right < limit.left:
bird.move(limit.w, 0)
if location.bottom < limit.top:
bird.move(0, limit.h)
bird.trail.append(bird.location.topleft)
bird.move(*get_delta(bird.course.angle, bird.course.magnitude))
for ii, position in enumerate(bird.trail):
copy = bird.get_current_frame().copy()
pixels = PixelArray(copy)
max_alpha = (float(ii) + 1) / len(bird.trail) * 100
for x in xrange(len(pixels)):
for y in xrange(len(pixels[0])):
h, s, l, a = Color(*copy.unmap_rgb(pixels[x][y])).hsla
if a:
color = Color(0, 0, 0)
color.hsla = self.trail_colors[ii].hsla[:3] + (max(0, int(float(a) / 100 * max_alpha)),)
pixels[x][y] = color
del pixels
self.get_display_surface().blit(copy, position)
self.trail_colors.rotate(-1)
else:
for bird in self.birds:
if not bird.offset and random() < .025:
bird.offset = choice(xrange(-1, 2)), choice(xrange(-1, 2))
bird.move(*bird.offset)
elif bird.offset and random() < .1:
bird.move(*[-x for x in bird.offset])
bird.offset = None
if not self.birds[0].is_playing() and random() < .05:
for bird in self.birds:
bird.play()
for bird in self.birds:
bird.update()
if self.birds[0].get_current_frameset().current_index == 0:
for bird in self.birds:
bird.halt()
bird.get_current_frameset().reset()
class Course:
def __init__(self, angle, magnitude):
self.angle = angle
self.magnitude = magnitude
def rotate(self, wall):
self.angle = wall * 2 - self.angle
class Best(Sprite):
def __init__(self, parent):
Sprite.__init__(self, parent)
self.set_low()
def set_low(self):
self.clear_frames()
low = None
for line in open(self.get_resource("scores")):
if low is None or int(line) < low:
low = int(line)
font = Font(self.get_resource("AlegreyaSansSC-Light.ttf"), 16)
for hue in xrange(0, 360, 60):
color = Color(0, 0, 0)
color.hsla = hue, 100, 40, 100
self.add_frame(font.render("b e s t : %.1f" % (low / 1000.0), True, color))
self.location.topright = self.get_display_surface().get_rect().topright
self.location.right -= 5
def add(self, time):
fp = open(self.get_resource("scores"), "a")
fp.write(str(time) + "\n")
fp.close()
self.set_low()