from os.path import join
from random import randint, randrange, choice
from math import sin, cos, radians, ceil
from collections import deque
from pygame import Surface, Rect, PixelArray
from pygame.image import load
from pygame.mask import from_surface
from pygame.draw import line, aaline
from pygame.transform import rotate
from pygame.locals import *
from lib.pgfw.pgfw.Animation import Animation
from lib.pgfw.pgfw.Sprite import Sprite
from lib.pgfw.pgfw.GameChild import GameChild
class Siphon(GameChild):
def __init__(self, parent):
GameChild.__init__(self, parent)
self.time_filter = self.get_game().time_filter
self.color_index = 0
self.load_configuration()
self.set_roots()
self.cancel()
self.set_nodesets()
self.set_score()
self.set_badges()
def load_configuration(self):
self.badge_size = 60, 44
self.stem_width = 30
self.set_colors()
def set_colors(self):
components = self.get_configuration("siphon", "root-colors")
colors = self.colors = []
for ii in xrange(0, len(components), 6):
colors.append((components[ii:ii + 3], components[ii + 3:ii + 6]))
def set_roots(self):
roots = self.roots = Roots(self, Roots.HORIZONTAL)
roots.add_initial(self.stem_width)
roots.init_surfaces(Rect(0, 0, self.stem_width, self.badge_size[1]))
def cancel(self):
self.contracting = False
self.releasing = False
self.release_elapsed = 0
self.angle_deviation = 0
def set_nodesets(self):
interpolator = self.get_game().interpolator
self.release_nodeset = interpolator.get_nodeset("release")
def set_score(self):
self.score = [[0, 0, 0] for _ in xrange(5)]
def set_badges(self):
self.badges = [Badge(self.roots, ii, self.badge_size) for ii in \
xrange(5)]
self.set_badge()
def set_badge(self):
points = self.get_points()
for ii, score in enumerate((2000, 5000, 10000, 16000)):
if points < score:
break
self.roots.set_badge(self.badges[ii])
def get_points(self):
points = 0
bases = 100, 200, 400, 800, 1600
for ii, level in enumerate(self.score):
base = bases[ii]
for jj in xrange(max(level)):
points += base * ((level[0] > jj) + (level[1] > jj) + \
(level[2] > jj))
base *= .9
return int(points)
def set_level(self, index=0):
self.cancel()
self.level = self.parent.levels[index]
self.planet_rect = self.level.planet.location
self.color_index = index
self.roots.place()
for badge in self.badges:
badge.place()
self.roots.gradient.set_tiles()
self.roots.gradient.set_frames()
self.draw_nodes()
def draw_nodes(self):
roots = self.roots
roots.clear_root_surface()
for node in roots.get_initial():
y = self.get_initial_y(node.id)
end = node.length, y
self.draw_line((0, y), end)
self.draw_children(node, end)
def get_initial_y(self, node_id):
modifier = -1 if node_id % 2 else 1
return self.roots.rect.h / 2 + modifier * 10 * ((node_id + 1) / 2)
def draw_line(self, start, end, alpha=180):
line(self.roots.root_surface, self.get_current_colors()[0], start, end,
3)
aaline(self.roots.root_surface, (255, 255, 255), start, end, 1)
def get_current_colors(self):
return self.colors[self.color_index]
def draw_children(self, node, start, depth=1):
for child in node.children:
end = self.get_offset_point(start, child.angle, child.length, True,
depth)
self.draw_line(start, end)
self.draw_children(child, end, depth + 1)
def get_offset_point(self, start, angle, length, deviate=False, depth=1):
if deviate:
angle -= self.angle_deviation * (angle - .8 ** depth * angle)
return int(round(start[0] + cos(radians(angle)) * length)), \
int(round(start[1] + sin(radians(angle)) * length))
def add(self, level):
offset = 70
length = [40, 52, 66, 84, 100][level]
for _ in xrange(25):
parent = self.get_random_parent()
for _ in xrange(3):
angle = randint(parent.angle - offset, parent.angle + offset)
if abs(angle) < 87 and (not parent.children or
abs(parent.children[0].angle - angle) >
45):
end = self.get_offset_point(self.get_end(parent), angle,
length)
if self.check_end(end):
self.increase_score(level)
roots = self.roots
roots.add(length, angle, parent)
self.resize_roots_rect(end)
self.roots.init_surfaces(roots.rect)
self.draw_nodes()
return True
def get_random_parent(self):
node = self.roots[randrange(0, self.roots.initial_count)]
while node.children:
if len(node.children) == 1:
if randint(0, 1):
break
node = choice(node.children)
return node
def get_end(self, node):
path = [node]
while node.parent is not None:
path.append(node.parent)
node = node.parent
x, y = node.length, self.get_initial_y(path.pop().id)
while path:
node = path.pop()
x, y = self.get_offset_point((x, y), node.angle, node.length)
return x, y
def check_end(self, end):
roots = self.roots.rect
planet = self.planet_rect
offset = end[0] + roots.left - planet.left, \
end[1] + roots.top - planet.top
if planet.move(-planet.left, -planet.top).collidepoint(offset):
return self.level.planet.frames[0].get_at(offset)[3] == 255
def increase_score(self, level):
self.score[level][randint(0, 2)] += 1
self.set_badge()
def resize_roots_rect(self, end):
roots = self.roots.rect
if end[0] > roots.w:
roots.w = end[0]
if end[1] < 0:
roots.inflate_ip(0, -end[1] * 2)
elif end[1] > roots.h:
roots.inflate_ip(0, (end[1] - roots.h) * 2)
def contract(self):
self.contracting = True
self.releasing = False
self.charge_elapsed = 0
self.roots.gradient.reverse()
def release(self):
if self.contracting:
self.contracting = False
self.releasing = True
self.release_elapsed = 0
self.roots.gradient.reverse()
def update(self):
self.update_angle_deviation()
if self.contracting or self.releasing:
self.draw_nodes()
self.roots.update()
def update_angle_deviation(self):
if self.contracting:
self.angle_deviation = self.level.food.charge
elif self.releasing:
self.release_elapsed += self.time_filter.get_last_frame_duration()
if self.release_elapsed > self.release_nodeset.get_length():
self.releasing = False
self.angle_deviation = 0
else:
self.angle_deviation = self.release_nodeset.get_y(
self.release_elapsed) * self.level.food.submitted_charge
class Roots(GameChild, list):
HORIZONTAL, VERTICAL = range(2)
def __init__(self, parent, orientation):
GameChild.__init__(self, parent)
self.display_surface = self.get_display_surface()
self.orientation = orientation
self.gradient = Gradient(self, 200)
def add(self, length, angle, parent=None):
self.append(Node(len(self), length, angle, parent))
if parent is not None:
parent.add_child(self[-1])
def add_initial(self, length):
count = self.initial_count = 5
for ii in xrange(count):
self.add(length, 0)
def get_initial(self):
return self[:self.initial_count]
def init_surfaces(self, rect):
surface = self.surface = Surface(rect.size)
surface.set_colorkey((0, 0, 0))
self.root_surface = Surface(rect.size)
self.rect = rect
self.gradient.set_frames()
def set_badge(self, badge):
self.badge = badge
def clear_root_surface(self):
self.root_surface.fill((0, 0, 0))
def place(self):
base = self.parent.level.planet.location
self.rect.midleft = base.left - self.parent.stem_width + 2, \
base.centery
def update(self):
self.badge.update()
self.gradient.update()
self.surface.blit(self.root_surface, (0, 0), None, BLEND_MIN)
self.display_surface.blit(self.surface, self.rect)
class Node:
def __init__(self, id, length, angle, parent=None):
self.id, self.length, self.angle, self.parent = id, length, angle, \
parent
self.children = []
def add_child(self, child):
self.children.append(child)
class Gradient(Sprite):
def __init__(self, parent, framerate):
Sprite.__init__(self, parent, framerate)
self.set_tiles()
def set_tiles(self):
tile_rect = Rect(0, 0, 16, 16)
colors = self.get_colors()
tiles = self.tiles = []
segment_count = len(colors)
segment_width = int(ceil(float(tile_rect.w) / segment_count))
for _ in xrange(segment_count):
frame = Surface(tile_rect.size)
x = 0
for color in colors:
frame.fill(color, (x, 0, segment_width, tile_rect.h))
x += segment_width
colors.rotate()
if self.parent.orientation == Roots.VERTICAL:
frame = rotate(frame, 90)
tiles.append(frame)
def get_colors(self):
count = 8
base_color = Color(*self.parent.parent.get_current_colors()[1])
bh, bs, bl, ba = base_color.hsla
bs_step = (100 - bs) / float(count - 1)
bl_step = (100 - bl) / float(count - 1)
colors = deque()
for _ in xrange(count):
color = Color(0, 0, 0)
color.hsla = map(int, (bh, min(100, bs), min(100, bl), ba))
colors.append(color)
bs += bs_step
bl += bl_step
return colors
def set_frames(self):
self.display_surface = self.parent.surface
index = 0 if not self.frames else \
self.get_current_frameset().get_current_id()
self.clear_frames()
rect = self.parent.rect
surface = Surface(rect.size)
if self.parent.orientation == Roots.VERTICAL:
surface = rotate(surface, 90)
rect = surface.get_rect()
for tile in self.tiles:
frame = surface.copy()
for x in xrange(0, rect.w, tile.get_width()):
for y in xrange(0, rect.h, tile.get_height()):
frame.blit(tile, (x, y))
self.add_frame(frame)
for _ in xrange(index):
self.shift_frame()
class Badge(Animation):
def __init__(self, parent, level, size):
Animation.__init__(self, parent)
self.level = level
self.rect = Rect((0, 0), size)
self.display_surface = self.get_display_surface()
self.background_color = Color(255, 222, 222)
self.set_background()
self.set_guns()
self.register(self.shift)
self.play(self.shift, 120)
def set_background(self):
width = 1
rect = self.rect
surface = Surface(rect.size)
colors = (0, 0, 0), (255, 255, 255)
for ii, x in enumerate(xrange(0, rect.w, width)):
surface.fill(colors[ii % 2], (x, 0, width, rect.h))
self.background = surface
self.surface = Surface(surface.get_size())
def set_guns(self):
width = 0
margin = 5
images = []
for guns in self.parent.parent.parent.gun_library:
image = guns[self.level].frames[0].copy()
mask = from_surface(image)
pixels = PixelArray(image)
for x in xrange(len(pixels)):
for y in xrange(len(pixels[0])):
if mask.get_at((x, y)):
pixels[x][y] = (0, 0, 0)
else:
pixels[x][y] = (255, 255, 255)
del pixels
width += image.get_width() + margin
images.append(image)
self.img = images[0]
surface = Surface((width, self.rect.h))
surface.fill((255, 255, 255))
x = 0
for image in images:
rect = image.get_rect()
rect.midleft = x, self.rect.h / 2
if self.level == 0:
rect.centery += 3
surface.blit(image, rect)
x += image.get_width() + margin
self.gun_surface = surface
self.gun_rect = surface.get_rect()
self.gun_rect.right = self.rect.w
def shift(self):
rect = self.gun_rect
rect.move_ip(2, 0)
if rect.left >= rect.w:
rect.left -= rect.w
def place(self):
if self.parent.orientation == Roots.HORIZONTAL:
self.rect.midright = self.parent.rect.midleft
else:
self.rect.midtop = self.parent.rect.midbottom
def update(self):
Animation.update(self)
gr = self.gun_rect
self.surface.fill(self.background_color)
self.background.set_colorkey((255, 255, 255))
self.surface.blit(self.background, (0, 0))
self.surface.blit(self.gun_surface, gr, None, BLEND_MIN)
self.surface.blit(self.gun_surface, gr.move(-gr.w, 0), None, BLEND_MIN)
self.display_surface.blit(self.surface, self.rect)
h, s, v, a = self.background_color.hsva
h += 2
if h > 360:
h -= 360
self.background_color.hsva = h, s, v, a