# -*- coding: utf-8 -*- from random import randint, randrange, choice from time import time from pygame import Surface, PixelArray, Rect from pygame.draw import aalines, polygon from pygame.font import Font from pygame.mixer import Sound from pygame.locals import * 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.extension import render_box class ElectricSieve(Game): def __init__(self): Game.__init__(self) self.background = Surface(self.display.screen.get_size()) self.background.fill((255, 80, 190)) self.title.activate() def set_children(self): Game.set_children(self) self.title = Title(self) self.sieve = Sieve(self) self.triangles = Triangles(self) self.acid = Acid(self) self.static = Static(self) def update(self): self.title.update() if self.triangles.active: self.display.screen.blit(self.background, (0, 0)) self.triangles.update() self.sieve.update() self.static.update() class Title(GameChild): def __init__(self, parent): GameChild.__init__(self, parent) self.display_surface = self.get_display_surface() self.delegate = self.parent.delegate bg_color = (255, 222, 173) self.background = surface = Surface(self.display_surface.get_size()) tile = Surface((2, 2)) tile.fill(bg_color) tile.set_at((0, 1), (220, 119, 41)) tile.set_at((1, 0), (220, 119, 41)) for y in xrange(0, surface.get_height(), 2): for x in xrange(0, surface.get_width(), 2): surface.blit(tile, (x, y)) # font = Font(self.get_resource("display", "title-font-path"), 20) # font.set_italic(True) # font.set_bold(True) # self.captions = captions = Sprite(self), Sprite(self) # colors = (0, 68, 170), (255, 255, 255), (128, 128, 128), \ # (220, 119, 41), (255, 80, 80), (0, 90, 110) # texts = ["", ""] # for ii, text in \ # enumerate(self.get_configuration("display", # "caption").upper().split()): # texts[ii] += "•" * (5 if ii else 3) # for ch in text: # texts[ii] += ch + " " # texts[ii] = texts[ii].strip() + "•" * (5 if ii else 3) # for _ in xrange(25): # color = choice(colors) # captions[0].add_frame(font.render(texts[0], True, color, (220, 208, 255))) # captions[1].add_frame(font.render(texts[1], True, color, (220, 208, 255))) # cx = self.display_surface.get_rect().centerx # captions[0].location.center = cx, 301 # captions[1].location.center = cx, 398 self.scoreboard = Scoreboard(self) self.music = Sound(self.get_resource("audio", "title")) self.advance = Sound(self.get_resource("audio", "title-advance")) self.subscribe(self.respond) def respond(self, event): if self.active: if self.delegate.compare(event, "advance"): self.deactivate() self.parent.triangles.activate() self.parent.sieve.activate() self.parent.static.activate() self.advance.play() def activate(self): self.active = True self.music.play(-1) def deactivate(self): self.active = False self.music.fadeout(500) def update(self): if self.active: self.display_surface.blit(self.background, (0, 0)) # for caption in self.captions: # caption.update() self.scoreboard.update() class Strip(Sprite): LEFT, RIGHT = range(2) def __init__(self, parent): Sprite.__init__(self, parent) self.deactivate() self.display_surface = self.get_display_surface() self.delegate = self.get_game().delegate self.hshifts = Shift(self, 1, "shift-2"), Shift(self, -1, "shift-2") self.add_frames() self.subscribe(self.respond) def deactivate(self): self.active = False def reset(self): for shift in self.hshifts: shift.reset() def add_frames(self): pass def respond(self, event): if self.active: compare = self.delegate.compare if compare(event, "left") or compare(event, "left", True): self.hshifts[self.LEFT].active = not event.cancel elif compare(event, "right") or compare(event, "right", True): self.hshifts[self.RIGHT].active = not event.cancel def activate(self): self.active = True def update(self): if self.active: for shift in self.hshifts: shift.update() if shift.time: self.move(shift.get_change()) Sprite.update(self) class Shift(GameChild): def __init__(self, parent, direction, nodeset): GameChild.__init__(self, parent) self.direction = direction self.reset() self.timer = self.get_game().time_filter self.nodeset = self.get_game().interpolator.get_nodeset(nodeset) def reset(self): self.active = False self.time = 0 def update(self): least, greatest = self.nodeset[0].x, self.nodeset[-1].x if self.active and self.time < greatest: self.time = min(self.time + self.timer.get_last_frame_duration(), greatest) elif not self.active and self.time > least: self.time = max(self.time - self.timer.get_last_frame_duration(), least) def get_change(self): return self.nodeset.get_y(self.time) * self.direction class Scoreboard(GameChild): BACKGROUND = 255, 255, 255 FOREGROUND = 27, 27, 27 NEW = 27, 27, 27 SPACING = 70 MARGIN = 30 BLINK_INTERVAL = 400 def __init__(self, parent): GameChild.__init__(self, parent) ds = self.display_surface = self.get_display_surface() self.scores_path = self.get_resource("score", "path") self.most_recent_score = None self.load() def load(self): scores = [] for line in file(self.scores_path, "r"): fields = line.split() scores.append((float(fields[0]), int(fields[1]), fields[2])) self.sprites = sprites = [] font_path = self.get_resource("display", "scoreboard-font-path") sizes = [14] * 5 for ii, score in enumerate(sorted(scores, key=lambda score: score[1], reverse=True)[:len(sizes)]): font = Font(font_path, sizes[ii]) sprites.append((Sprite(self, self.BLINK_INTERVAL), Sprite(self, self.BLINK_INTERVAL))) score_text = str(score[1]) color = self.FOREGROUND if not (score[1] == self.most_recent_score) else \ self.BACKGROUND score_plate = font.render(score_text, False, color, self.BACKGROUND) rect = score_plate.get_rect() surface = Surface(rect.inflate((2, 2)).size) surface.fill(self.FOREGROUND) rect.center = surface.get_rect().center surface.blit(score_plate, rect) sprites[ii][1].add_frame(render_box(font, score_text, False, color, self.BACKGROUND, self.FOREGROUND, 1, 2)) sprites[ii][0].add_frame(render_box(font, score[2], False, color, self.BACKGROUND, self.FOREGROUND, 1, 2)) if score[1] == self.most_recent_score: sprites[ii][1].add_frame(render_box(font, score_text, False, self.NEW, self.BACKGROUND, self.FOREGROUND, 1, 2)) sprites[ii][0].add_frame(render_box(font, score[2], False, self.NEW, self.BACKGROUND, self.FOREGROUND, 1, 2)) sprites[ii][0].location.left = self.MARGIN sprites[ii][1].location.right = self.get_display_surface().get_rect().right - self.MARGIN y = self.get_display_surface().get_rect().centery + self.SPACING * (ii - len(sizes) / 2) for sprite in sprites[ii]: sprite.location.centery = y def write(self): score = int(round(self.get_game().triangles.score)) file(self.scores_path, "a").write(str(time()) + " " + str(score) + " " + "SIV" + "\n") self.most_recent_score = score self.load() def update(self): for pair in self.sprites: for sprite in pair: sprite.update() class Sieve(Strip): UP, DOWN = range(2) def __init__(self, parent): Strip.__init__(self, parent) self.delegate = self.get_game().delegate self.electric = Electric(self) self.add_location(offset=(self.location.w, 0)) def add_frames(self): bar_locations = [] self.bar_rects = bar_rects = [] x = 0 sh = 30 nodeset = self.get_game().interpolator.get_nodeset("scale") self.bar_w = bar_w = 3 self.gaps = gaps = [] while x < nodeset[-1].x: bar_locations.append(x) bar_rects.append(Rect(x, 0, bar_w, sh)) gaps.append(nodeset.get_y(x, natural=True)) x += gaps[-1] surface = Surface((x, sh)) transparent_color = (255, 0, 255) surface.fill(transparent_color) surface.set_colorkey(transparent_color) frames = surface, surface.copy() colors = (0, 255, 0), (153, 0, 204) for x in bar_locations: bar_rects.append(Rect(x + surface.get_width(), 0, bar_w, sh)) for ii, frame in enumerate(frames): frame.fill(colors[ii], (x, 0, bar_w, sh)) frame.fill(colors[ii - 1], (x + 1, 1, 1, sh - 2)) for frame in frames: self.add_frame(frame) def reset(self): Strip.reset(self) self.location.centerx = self.display_surface.get_rect().centerx self.locations[1].centerx = self.location.centerx + self.location.w def update(self): if self.active: if self.location.right < 0: self.move(self.location.w) if self.locations[1].left > self.display_surface.get_width(): self.move(-self.location.w) for location in self.locations: location.bottom = self.parent.acid.get_top() self.electric.location.centery = self.location.centery + 13 self.electric.update() for rect in self.bar_rects: rect.centery = self.location.centery Strip.update(self) class Electric(Sprite): def __init__(self, parent): Sprite.__init__(self, parent) self.display_surface = self.get_display_surface() self.add_frames() def add_frames(self): surface = Surface((self.display_surface.get_width(), self.parent.location.h - 10)) frames = surface, surface.copy() colors = (255, 255, 0), (100, 89, 213) pixel_arrays = PixelArray(frames[0]), PixelArray(frames[1]) for x in xrange(len(pixel_arrays[0])): for y in xrange( len(pixel_arrays[0][0])): pixel_arrays[0][x][y] = colors[(y + x) % 2] pixel_arrays[1][x][y] = colors[(y + x + 1) % 2] for pixels in pixel_arrays: del pixels for frame in frames: self.add_frame(frame) class Triangles(GameChild, list): def __init__(self, parent): GameChild.__init__(self, parent) self.music = Sound(self.get_resource("audio", "triangles")) self.deactivate() self.display_surface = self.get_display_surface() self.delegate = self.get_game().delegate self.booster = Shift(self, 1, "boost") self.hit = Sound(self.get_resource("audio", "hit")) self.miss = Sound(self.get_resource("audio", "miss")) self.reset() self.subscribe(self.respond) def deactivate(self): self.active = False self.music.fadeout(500) def reset(self): list.__init__(self, []) self.streak = 0 self.score = 0 self.booster.reset() def populate(self): if not self: self.append(Triangle(self)) self[-1].location.bottom = 0 self.set_next_gap() while self[-1].location.top > -self.display_surface.get_height(): self.append(Triangle(self)) self[-1].location.bottom = self[-2].location.top - self.next_gap self.set_next_gap() def set_next_gap(self): self.next_gap = randint(500, 800) def respond(self, event): if self.active: compare = self.delegate.compare if compare(event, "down") or compare(event, "down", True): self.booster.active = not event.cancel def get_boost(self): return self.booster.get_change() def activate(self): self.active = True self.music.play(-1, 0, 500) def update(self): if self.active: self.populate() self.booster.update() if self[0].location.collidelist(self.parent.sieve.locations) != -1: sieve = self.parent.sieve if self[0].location.colliderect(sieve.electric.location): self.parent.acid.increase() self.streak += 1 self.score += self.streak ** .8 + \ self.parent.acid.get_volume() * 5 + \ self[0].count self.remove(self[0]) self.hit.play() else: for br in sieve.bar_rects: for tr in self[0].collision_rects: if tr.move((self[0].location.left, 0)).colliderect(br.move((sieve.location.left, 0))): self.remove(self[0]) self.parent.static.increase() self.streak = 0 self.miss.play() break for triangle in self: triangle.update() class Triangle(Sprite): def __init__(self, parent): Sprite.__init__(self, parent) mark = randint(112, 328) sieve = self.parent.parent.sieve gaps = sieve.gaps start = randrange(0, len(gaps)) widths = [gaps[start]] while sum(widths) < mark: widths.append(gaps[(start + len(widths)) % len(gaps)]) surface = Surface((sum(widths), 20)) surface.set_colorkey((0, 0, 0)) x = 0 height = surface.get_height() margin = 26 self.collision_rects = collision_rects = [] for width in widths: x += sieve.bar_w points = (x + margin / 2, height - 2), \ (x + width - margin / 2 - 1, height - 2), \ (x + width / 2.0, 1) polygon(surface, (60, 255, 220), points) collision_rects.append(Rect(points[0], (width - margin - 1, 1))) x += width - sieve.bar_w self.add_frame(surface) self.location.centerx = self.get_display_surface().get_rect().centerx self.count = len(widths) def update(self): self.move(dy=9.5 * self.get_game().acid.get_volume() + 3.8 + \ self.parent.get_boost()) for rect in self.collision_rects: rect.bottom = self.location.bottom Sprite.update(self) class Acid(GameChild): def __init__(self, parent): GameChild.__init__(self, parent) self.display_surface = self.get_display_surface() self.level_r = 80, 320 self.nodeset = self.get_game().interpolator.get_nodeset("volume") self.reset() def reset(self): self.substance = 0 def get_top(self): return self.display_surface.get_height() - self.get_level() def get_level(self): return self.get_volume() * (self.level_r[1] - self.level_r[0]) + \ self.level_r[0] def get_volume(self): return self.nodeset.get_y(self.substance) def increase(self): self.substance += 1 class Static(Sprite): def __init__(self, parent): Sprite.__init__(self, parent, 120) self.noise = Sound(self.get_resource("audio", "noise")) self.end = Sound(self.get_resource("audio", "end")) self.deactivate() self.delegate = self.get_game().delegate self.increaser = Shift(self, 1, "intensity") self.total = Total(self) self.reset() self.add_frames() self.subscribe(self.respond) def deactivate(self): self.active = False self.end.fadeout(500) def reset(self): self.complete = False self.intensity = 0 self.noise.set_volume(0) self.increaser.reset() def add_frames(self): surface = Surface(self.get_display_surface().get_size()) frames = surface, surface.copy(), surface.copy(), surface.copy() tiles = [] for _ in xrange(32): tiles.append(Surface((16, 16))) pixel_arrays = [] for tile in tiles: pixel_arrays.append(PixelArray(tile)) colors = (0, 0, 0), (64, 64, 64), (128, 128, 128), (196, 196, 196), \ (255, 255, 255) for x in xrange(len(pixel_arrays[0])): for y in xrange(len(pixel_arrays[0][0])): for pixels in pixel_arrays: pixels[x][y] = choice(colors) for pixels in pixel_arrays: del pixels del pixel_arrays for frame in frames: for y in xrange(0, frame.get_height(), tiles[0].get_height()): for x in xrange(0, frame.get_width(), tiles[0].get_width()): frame.blit(choice(tiles), (x, y)) self.add_frame(frame) def respond(self, event): if self.active and self.complete: if self.delegate.compare(event, "advance"): self.parent.title.scoreboard.write() self.total.deactivate() self.deactivate() self.reset() self.parent.acid.reset() self.parent.triangles.reset() self.parent.sieve.reset() self.parent.title.activate() def increase(self): self.intensity += self.increaser.get_change() if self.intensity > 1: self.intensity = 1 self.increaser.time += 12000 if self.increaser.time >= self.increaser.nodeset[-1].x + 5000: self.increaser.time = self.increaser.nodeset[-1].x + 5000 def activate(self): self.active = True self.noise.play(-1) def update(self): if self.active: if not self.complete and self.intensity >= .65: self.complete = True self.parent.sieve.deactivate() self.parent.triangles.deactivate() self.set_alpha(255) self.noise.fadeout(6000) self.end.play(-1, 0, 4000) self.total.load() elif not self.complete: self.set_alpha(int(self.intensity * 255)) if self.intensity > 0: self.intensity *= .998 self.increaser.update() self.noise.set_volume(self.intensity) Sprite.update(self) self.total.update() class Total(Sprite): def __init__(self, parent): Sprite.__init__(self, parent, 68) self.deactivate() self.font = Font(self.get_resource("display", "score-font-path"), 48) self.font.set_italic(True) def deactivate(self): self.active = False def load(self): self.clear_frames() score = "" for ch in str(int(round(self.get_game().triangles.score))): score += ch + " " colors = (255, 255, 80), (80, 255, 255), (255, 80, 255), \ (255, 120, 60), (60, 255, 120), (120, 60, 255) template = Surface((self.display_surface.get_width(), 100)) transparent_color = (255, 0, 255) template.fill(transparent_color) template.set_colorkey(transparent_color) tr = template.get_rect() template.fill((255, 0, 0), (0, 20, tr.w, 1)) template.fill((255, 128, 128), (0, 21, tr.w, 1)) for y in xrange(22, 78, 2): template.fill((255, 255, 255), (0, y, tr.w, 1)) template.fill((255, 128, 128), (0, 78, tr.w, 1)) template.fill((255, 0, 0), (0, 79, tr.w, 1)) for _ in xrange(20): surface = template.copy() polygon(surface, choice(colors), ((tr.centerx - 7, 19), (tr.centerx, 0), (tr.centerx + 7, 19))) text = self.font.render(score, True, choice(colors)) rect = text.get_rect() rect.center = tr.centerx, tr.centery + 2 surface.blit(text, rect) polygon(surface, choice(colors), ((tr.centerx - 7, 80), (tr.centerx, tr.h - 1), (tr.centerx + 7, 80))) self.add_frame(surface) self.location.center = self.display_surface.get_rect().center self.active = True def update(self): if self.active: Sprite.update(self)