from sys import platform


if __name__ == "__main__":
    if platform == "darwin":
        from setuptools import setup, find_packages
        from lib.pgfw.pgfw.Configuration import Configuration
        from lib.pgfw.pgfw.Setup import Setup
        config = Configuration()
        setup_obj = Setup()
        version = config.get_section("setup")["version"]
        name = setup_obj.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="OPEN-GAME", plist=plist)],
              setup_requires=["py2app"],
              options=dict(py2app=dict(arch="i386",)),
              data_files=["PlanetLicker.py", "resource", "lib", "config", "FreeLicense.txt"])
    elif platform == "win32":
        from lib.pgfw.pgfw.SetupWin import SetupWin
        SetupWin().setup()
    else:
        from lib.pgfw.pgfw.Setup import Setup
        Setup().setup()
from os.path import join, basename
from glob import glob
from random import random, choice, randrange, randint, shuffle
from math import degrees

from pygame import Surface, Color
from pygame.draw import circle, polygon
from pygame.gfxdraw import aacircle
from pygame.image import load
from pygame.transform import rotate
from pygame.event import clear
from pygame.font import Font
from pygame.mixer import Sound, music
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, BlinkingSprite, RainbowSprite
from lib.pgfw.pgfw.Vector import Vector
from lib.pgfw.pgfw.Animation import Animation
from lib.pgfw.pgfw.extension import (get_distance, get_angle, get_delta, get_endpoint,
                                     get_hue_shifted_surface, fill_tile, get_shadowed_text,
                                     get_hsla_color)

class PlanetLicker(Game, Animation):

    SPAWN_MARGIN = 200
    LICK_A, LICK_B, LICK_C = "lick-a", "lick-b", "lick-c"
    STAR_SATURATION = 60
    STAR_COLORED_LIGHTNESS = 50
    STAR_NEUTRAL_LIGHTNESS = 80
    FADE_OUT_TITLE_LENGTH = 4000
    FADE_IN_TITLE_LENGTH = 1000

    def __init__(self):
        Game.__init__(self)
        Animation.__init__(self, self)
        self.sound_effects = SoundEffects(self)
        self.display_surface = self.get_display_surface()
        self.title = Title(self)
        self.monster = Monster(self)
        self.planets = Planets(self)
        self.hud = HUD(self)
        self.bgm = Sound(self.get_resource("Lick-the-World.ogg"))
        self.subscribe(self.respond)
        self.register(self.reset, self.unsuppress_commands)
        self.reset()

    def set_background(self):
        field = []
        w, h = [Planets.WORLD_SIZE] * 2
        for x in xrange(w):
            field.append([])
            for y in xrange(h):
                if random() < .0005:
                    field[-1].append((randrange(0, 360), choice((10, 20, 30, 40, 50))))
                else:
                    field[-1].append(None)
        background = self.background = Sprite(self, 800)
        color = Color(0, 0, 0)
        for _ in xrange(8):
            frame = Surface((w, h))
            frame.fill((0, 0, 0))
            for x, row in enumerate(field):
                for y, cell in enumerate(row):
                    if cell:
                        rect = Rect((0, 0), [randint(1, 2)] * 2)
                        rect.center = x, y
                        if random() < .5:
                            color.hsla = (cell[0] + randint(-cell[1], cell[1])) % 360, \
                                         self.STAR_SATURATION, self.STAR_COLORED_LIGHTNESS, \
                                         100
                        else:
                            color.hsla = 0, 0, self.STAR_NEUTRAL_LIGHTNESS, 100
                        for ox, oy in (0, 0), (-w, 0), (0, -h), (w, 0), (0, h):
                            frame.fill(color, rect.move(ox, oy))
                            frame.fill(color, rect.move(-1 + ox, -1 + oy))
                            frame.fill(color, rect.move(1 + ox, 1 + oy))
                            frame.fill(color, rect.move(-1 + ox, 1 + oy))
                            frame.fill(color, rect.move(1 + ox, -1 + oy))
                            frame.fill(color, rect.move(-2 + ox, 0 + oy))
                            frame.fill(color, rect.move(2 + ox, 2 + oy))
                            frame.fill(color, rect.move(2 + ox, 0 + oy))
                            frame.fill(color, rect.move(-2 + ox, -2 + oy))
            background.add_frame(frame)
        background.add_location(offset=(w, 0))
        background.add_location(offset=(0, h))
        background.add_location(offset=(w, h))

    def respond(self, event):
        if not self.title.active and not self.commands_suppressed and not self.game_over:
            compare = self.get_game().delegate.compare
            if compare(event, "reset-game"):
                self.reset()
            elif self.compare_lick(event, self.LICK_A):
                self.add_vector(self.LICK_A)
            elif self.compare_lick(event, self.LICK_B):
                self.add_vector(self.LICK_B)
            elif self.compare_lick(event, self.LICK_C):
                self.add_vector(self.LICK_C)

    def unsuppress_commands(self):
        self.commands_suppressed = False

    def compare_lick(self, event, lick):
        compare = self.get_game().delegate.compare
        if self.check_command_line("-makey"):
            return (lick == self.LICK_A and compare(event, "left")) or \
                (lick == self.LICK_B and compare(event, "up")) or \
                (lick == self.LICK_C and compare(event, "right"))
        else:
            return compare(event, lick)

    def add_vector(self, lick):
        closest = None
        min_distance = None
        for planet in self.planets.planets:
            if planet.lick == lick:
                for location in planet.locations:
                    distance = get_distance(location.center, self.monster.location.center)
                    if min_distance is None or distance < min_distance:
                        closest = location
                        min_distance = distance
        if closest is None:
            self.get_game().sound_effects.play("stall")
        else:
            planets = self.monster.collide_with_planets()
            if planets and lick in [planet.lick for planet in planets]:
                for planet in planets:
                    if planet.lick == lick:
                        break
                self.monster.show_lick()
                planet.shrink()
            else:
                angle = get_angle(closest.center, self.monster.location.center)
                vector = Motion(self, degrees(angle))
                self.vectors.append(vector)
                self.get_game().sound_effects.play("propell-2")

    def reset(self):
        self.time_filter.close()
        self.game_over = False
        self.set_background()
        self.planets.reset()
        self.monster.reset()
        self.title.reset()
        self.halt()
        self.obstacles = []
        self.vectors = []
        while True:
            conflict = False
            self.move(randint(0, Planets.WORLD_SIZE),
                      randint(0, Planets.WORLD_SIZE))
            for planet in self.planets.planets:
                for location in planet.locations:
                    if get_distance(location.center, self.monster.location.center) < \
                       self.SPAWN_MARGIN:
                        conflict = True
                        break
                if conflict:
                    break
            if not conflict:
                break
        self.suppress_commands(2000)
        clear()
        self.monster.get_current_frameset().reset()
        self.time_filter.open()

    def move(self, x, y):
        w, h = self.background.location.size
        self.background.move(-x, -y)
        if self.background.location.top > 0:
            self.background.move(dy=-h)
        if self.background.location.right < 0:
            self.background.move(w)
        if self.background.location.bottom < 0:
            self.background.move(dy=h)
        if self.background.location.left > 0:
            self.background.move(-w)
        for planet in self.planets.planets:
            planet.move(-x, -y)
            if planet.location.top > 0:
                planet.move(dy=-h)
            if planet.location.right < 0:
                planet.move(w)
            if planet.location.bottom < 0:
                planet.move(dy=h)
            if planet.location.left > 0:
                planet.move(-w)
        for obstacle in self.obstacles:
            obstacle.move(-x, -y)
            if obstacle.location.top > 0:
                obstacle.move(dy=-h)
            if obstacle.location.right < 0:
                obstacle.move(w)
            if obstacle.location.bottom < 0:
                obstacle.move(dy=h)
            if obstacle.location.left > 0:
                obstacle.move(-w)

    def suppress_commands(self, length):
        self.commands_suppressed = True
        self.reset_timer(self.unsuppress_commands)
        self.time_filter.reset_ticks()
        self.play(self.unsuppress_commands, delay=length, play_once=True)

    def add_obstacles(self, origin):
        for _ in xrange(6):
            obstacle = Obstacle(self)
            obstacle.load_from_path(self.get_resource("mine"), True)
            while True:
                obstacle.location.center = get_endpoint(origin, randrange(0, 360),
                                                        randint(190, 200))
                conflict = False
                for planet in self.planets.planets:
                    if planet.location.colliderect(obstacle.location):
                        conflict = True
                        break
                for other in self.obstacles:
                    if other.location.colliderect(obstacle.location.inflate(20, 20)):
                        conflict = True
                        break
                if not conflict:
                    break
            obstacle.add_location(offset=(Planets.WORLD_SIZE, 0))
            obstacle.add_location(offset=(0, Planets.WORLD_SIZE))
            obstacle.add_location(offset=(Planets.WORLD_SIZE, Planets.WORLD_SIZE))
            self.obstacles.append(obstacle)

    def end_game(self, success=False):
        self.game_over = True
        font = Font(self.get_resource("PetMe.ttf"), 64)
        color = (255, 0, 0)
        if success:
            frame = font.render("YOU WIN!", True, (0, 255, 0))
            self.get_game().sound_effects.play("win")
        else:
            frame = font.render("GAME OVER", True, (255, 0, 0))
        sprite = self.game_over_sprite = Sprite(self)
        sprite.add_frame(frame)
        sprite.location.center = self.display_surface.get_rect().center
        self.play(self.reset, delay=3000, play_once=True)

    def update(self):
        Animation.update(self)
        self.title.update()
        if not self.title.active:
            for vector in self.vectors:
                self.move(*vector.delta)
                vector.update()
                if vector.delta == (0, 0):
                    self.vectors.remove(vector)
            if not self.vectors and self.monster.get_current_frameset().name != "lick":
                self.monster.set_frameset("idle")
            elif self.monster.get_current_frameset().name != "lick":
                self.monster.set_frameset("moving")
            self.background.update()
            self.planets.update()
            damaged = False
            for obstacle in self.obstacles:
                for location in obstacle.locations:
                    if location.colliderect(self.monster.location) and \
                       get_distance(location.center, self.monster.location.center) < \
                       location.w / 2 + self.monster.location.w / 2:
                        if not damaged:
                            self.monster.damage()
                            damaged = True
                        self.obstacles.remove(obstacle)
                obstacle.update()
            self.hud.update()
            self.monster.update()
            self.show_hints()
            if self.game_over:
                self.game_over_sprite.update()

    def show_hints(self):
        for lick in Planets.LICKS:
            closest = None
            min_distance = None
            for planet in self.planets.planets:
                for location in planet.locations:
                    if planet.lick == lick:
                        distance = get_distance(self.monster.location.center,
                                                location.center)
                        if not closest or distance < min_distance:
                            color = planet.get_color()
                            closest = location
                            min_distance = distance
            rect = self.get_display_surface().get_rect()
            if closest and not closest.colliderect(rect.inflate(-20, -20)):
                surface = Surface((20, 30))
                surface.fill((255, 255, 255))
                transparent = (255, 0, 255)
                surface.set_colorkey(transparent)
                polygon(surface, transparent, ((0, 10), (0, 0), (10, 0)))
                polygon(surface, transparent, ((10, 0), (20, 0), (20, 10)))
                circle(surface, color, (10, 20), 8)
                if closest.centery < 0 or closest.centery > rect.h:
                    x = closest.centerx
                    if x < 0:
                        x = 0
                    elif x > rect.w - surface.get_width():
                        x = rect.w - surface.get_width()
                    if closest.centery < 0:
                        y = 0
                    else:
                        y = rect.h - surface.get_height()
                        surface = rotate(surface, 180)
                else:
                    y = closest.centery
                    if closest.centery < 0:
                        y = 0
                    elif closest.centery > rect.h - surface.get_height():
                        y = rect.h - surface.get_height()
                    if closest.centerx < 0:
                        x = 0
                        surface = rotate(surface, 90)
                    else:
                        x = rect.w - surface.get_height()
                        surface = rotate(surface, 270)
                self.get_display_surface().blit(surface, (x, y))


class Title(Animation):

    PLANET_MARGIN = 53
    TITLE_OFFSET = 11, 11
    BLINK_INTERVAL = 500
    EATEN_SOUND_DELAY = 600
    LICK_LENGTH = 500

    def __init__(self, parent):
        Animation.__init__(self, parent)
        self.display_surface = self.get_display_surface()
        self.audio = Sound(self.get_resource("Lick-Through.ogg"))
        self.set_monster()
        self.set_text()
        self.set_background()
        self.set_planets()
        self.subscribe(self.respond)
        self.register(self.deactivate, self.play_gone_sound_effect, self.end_lick)

    def set_monster(self):
        monster = self.monster = Sprite(self)
        monster.load_from_path(self.get_resource("title"), transparency=True, query="biomass-*.png")
        order = [0, 1, 0, 1, 0, 2, 0, 3, 1, 3, 1, 3, 4, 3]
        interval = [2000, 300, 3000, 300, 2000, 300, 1000, 2000, 300, 3000, 300, 2000, 300, 2000]
        monster.add_frameset(order, interval, "idle", True)
        monster.add_frameset([5], name="lick")

    def set_text(self):
        name = self.name = RainbowSprite(self, load(self.get_resource("title/name.png")).convert_alpha(),
                                         60)
        name.location.topleft = self.TITLE_OFFSET
        prompt = self.prompt = BlinkingSprite(self, self.BLINK_INTERVAL)
        font = Font(self.get_resource("PetMe128.ttf"), 12)
        prompt.add_frame(get_shadowed_text("LICK TO START", font, (0, 2), (255, 255, 255), True))
        prompt.location.midtop = name.location.centerx, name.location.bottom + 15

    def set_background(self):
        background = self.background = Sprite(self)
        base_frame = Surface(self.display_surface.get_size())
        fr = base_frame.get_rect()
        base_column = load(self.get_resource("title/column.png")).convert()
        cr = base_column.get_rect()
        color = Color(0, 0, 0)
        for hue_offset in xrange(0, 360, 4):
            column = get_hue_shifted_surface(base_column, hue_offset)
            frame = base_frame.copy()
            fill_tile(frame, column)
            background.add_frame(frame)

    def set_planets(self):
        planets = self.planets = []
        rect = self.get_display_surface().get_rect()
        for ii, path in enumerate(sorted(glob(join(self.get_resource("title/"), "planet-*.png")))):
            planet = Sprite(self)
            planet.load_from_path(path, transparency=True)
            planet.location.centerx = rect.centerx - (1 - ii) * (self.PLANET_MARGIN + planet.location.w)
            planet.location.bottom = rect.bottom
            planets.append(planet)

    def respond(self, event):
        if self.active and not self.get_game().commands_suppressed:
            compare = self.get_game().compare_lick
            if compare(event, PlanetLicker.LICK_A):
                self.planets[0].hide()
                self.lick()
            elif compare(event, PlanetLicker.LICK_B):
                self.planets[1].hide()
                self.lick()
            elif compare(event, PlanetLicker.LICK_C):
                self.planets[2].hide()
                self.lick()
            if all(planet.is_hidden() for planet in self.planets):
                self.play(self.deactivate, delay=1000, play_once=True)
                self.get_game().suppress_commands(2000)

    def lick(self):
        self.monster.set_frameset("lick")
        self.play(self.end_lick, delay=self.LICK_LENGTH, play_once=True)
        self.play_lick_menu_sound()

    def end_lick(self):
        self.monster.set_frameset("idle")

    def play_lick_menu_sound(self):
        self.get_game().sound_effects.play("licking")
        self.play(self.play_gone_sound_effect, delay=self.EATEN_SOUND_DELAY, play_once=True)

    def play_gone_sound_effect(self):
        self.get_game().sound_effects.play("gone")

    def reset(self):
        self.activate()
        self.monster.location.center = self.get_display_surface().get_rect().center
        for planet in self.planets:
            planet.unhide()
        self.halt()
        self.monster.set_frameset("idle")

    def activate(self):
        self.active = True
        self.audio.play(-1, 0, PlanetLicker.FADE_IN_TITLE_LENGTH)
        self.get_game().bgm.fadeout(PlanetLicker.FADE_IN_TITLE_LENGTH)

    def deactivate(self):
        self.active = False
        self.audio.fadeout(PlanetLicker.FADE_OUT_TITLE_LENGTH)
        self.get_game().bgm.play(-1, 0, PlanetLicker.FADE_IN_TITLE_LENGTH)
        self.get_game().sound_effects.play("start")

    def update(self):
        if self.active:
            Animation.update(self)
            self.background.update()
            self.name.update()
            # self.prompt.update()
            self.monster.update()
            for planet in self.planets:
                planet.update()


class Obstacle(Sprite):

    def __init__(self, parent):
        Sprite.__init__(self, parent, 300)
        self.alpha_base = 0

    def update(self):
        Sprite.update(self)
        self.alpha_base = (self.alpha_base + 48) % 511
        for location in self.locations:
            for radius in xrange(int(self.location.w / 4.0), 2, -2):
                alpha = int((255 * 0 + .875 * abs(self.alpha_base - 255)) * (1 - (radius / (self.location.w / 4.0))))
                surface = Surface(self.location.inflate(10, 10).size, SRCALPHA)
                circle(surface, (255, 192, 128, alpha), surface.get_rect().center, radius)
                ds = self.get_display_surface()
                ds.blit(surface, location.inflate(10, 10).topleft)


class SoundEffects(GameChild):

    def __init__(self, parent):
        GameChild.__init__(self, parent)
        effects = self.effects = []
        for path in glob(join(self.get_resource("sfx"), "*.wav")):
            effects.append(SoundEffect(self, path))

    def play(self, name, volume=None):
        for effect in self.effects:
            if effect.name == name:
                effect.play(volume=volume)


class SoundEffect(GameChild, Sound):

    DEFAULT_VOLUME = 1.0

    def __init__(self, parent, path, volume=DEFAULT_VOLUME):
        GameChild.__init__(self, parent)
        Sound.__init__(self, path)
        self.name = basename(path).split(".")[0]
        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, volume=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 Motion(GameChild):

    def __init__(self, parent, angle):
        GameChild.__init__(self, parent)
        self.angle = angle
        self.nodeset = self.get_game().interpolator.get_nodeset("speed")
        self.time_left = self.nodeset.get_length()
        self.update()

    def update(self):
        if self.time_left > 0:
            self.time_left -= self.get_game().time_filter.get_last_frame_duration()
            dx, dy = get_delta(self.angle, self.nodeset.get_y(self.time_left))
            self.delta = -dx, dy
        else:
            self.delta = 0, 0


class Monster(Sprite):

    INITAL_LIFE = 3

    def __init__(self, parent):
        Sprite.__init__(self, parent)
        self.display_surface = self.get_display_surface()
        self.add_frames()
        self.location.center = self.display_surface.get_rect().center
        self.register(self.end_lick)

    def add_frames(self):
        self.load_from_path(self.get_resource("biomass/"), True)
        self.add_frameset([0, 3, 4, 0, 5], [5000, 800, 800, 5000, 200], "idle", True)
        self.add_frameset([0, 1, 2, 1], 100, "lick")
        self.add_frameset(0, name="moving")
        # self.add_frameset([3, 4], [400, 800], "shifty")
        # self.add_frameset(5, name="cute")

    def reset(self):
        self.life = self.INITAL_LIFE
        self.halt(self.end_lick)
        self.set_frameset("idle")

    def collide_with_planets(self):
        planets = []
        for planet in self.get_game().planets.planets:
            for location in planet.locations:
                if location.colliderect(self.location) and \
                   get_distance(self.location.center, location.center) < \
                   location.w / 2 + self.location.w / 2:
                    planets.append(planet)
        return planets

    def damage(self):
        self.life -= 1
        if self.life == 0:
            self.get_game().end_game()
            self.get_game().sound_effects.play("player_death")
        else:
            self.get_game().sound_effects.play("take_damage-1")

    def show_lick(self):
        if not self.get_current_frameset().name == "lick":
            self.set_frameset("lick")
            self.play(self.end_lick, delay=800, play_once=True)
            self.get_game().sound_effects.play("licking")

    def end_lick(self):
        self.set_frameset("idle")

    def update(self):
        Sprite.update(self)


class Planets(GameChild):

    WORLD_SIZE = 1500
    RADII = 64
    LICKS = PlanetLicker.LICK_A, PlanetLicker.LICK_B, PlanetLicker.LICK_C
    CITY, VOLCANO, UNDERWATER, BEACH, ARCADE, GHOST = xrange(6)
    MIN_MARGIN = 400
    MAX_TRIES = 800
    MIN_PLANETS = 3

    def __init__(self, parent):
        GameChild.__init__(self, parent)

    def reset(self):
        self.populate()

    def populate(self):
        kinds = range(6)
        kinds_copy = list(kinds)
        for _ in xrange(3):
            kinds.append(kinds_copy.pop())
        radii = list([self.RADII] * 9)
        shuffle(radii)
        licks = list(self.LICKS + self.LICKS + self.LICKS)
        freeze_avoided = False
        while not freeze_avoided:
            planets = self.planets = []
            for kind, radius, lick in zip(kinds, radii, licks):
                tries = 0
                while True:
                    conflict = False
                    planet = Planet(self, radius, lick, kind)
                    planet.location.center = randint(0, Planets.WORLD_SIZE), \
                                             randint(0, Planets.WORLD_SIZE)
                    planet.add_location(offset=(Planets.WORLD_SIZE, 0))
                    planet.add_location(offset=(0, Planets.WORLD_SIZE))
                    planet.add_location(offset=(Planets.WORLD_SIZE, Planets.WORLD_SIZE))
                    for other in self.planets:
                        for location in other.locations:
                            for my_location in planet.locations:
                                tries += 1
                                if get_distance(location.center, my_location.center) < self.MIN_MARGIN:
                                    conflict = True
                                    break
                            if conflict:
                                break
                        if conflict:
                            break
                    if not conflict or tries > self.MAX_TRIES:
                        break
                planets.append(planet)
                if tries > self.MAX_TRIES:
                    break
            if tries < self.MAX_TRIES:
                freeze_avoided = True
                
    def update(self):
        for planet in self.planets:
            planet.update()


class Planet(Sprite):

    COLORS = (251, 186, 68), (60, 133, 255), (60, 219, 90)
    SHRINK_STEP = 16
    CANVAS_SIZE = 160

    def __init__(self, parent, radius, lick, kind):
        Sprite.__init__(self, parent)
        self.max_radius = radius
        self.radius = radius
        self.lick = lick
        self.kind = kind
        self.set_ring()
        self.add_frames()

    def set_ring(self):
        ring = self.ring = None
        if self.kind == Planets.CITY:
            self.ring = load(self.get_resource("planets/city_128.png")).convert_alpha()
        elif self.kind == Planets.UNDERWATER:
            self.ring = load(self.get_resource("planets/underwater_128.png")).convert_alpha()
        elif self.kind == Planets.GHOST:
            self.ring = load(self.get_resource("planets/ghost_128.png")).convert_alpha()
        elif self.kind == Planets.BEACH:
            self.ring = load(self.get_resource("planets/beach_128.png")).convert_alpha()
        elif self.kind == Planets.VOLCANO:
            self.ring = load(self.get_resource("planets/volcano_128.png")).convert_alpha()
        elif self.kind == Planets.ARCADE:
            self.ring = load(self.get_resource("planets/arcade_128.png")).convert_alpha()

    def add_frames(self):
        self.clear_frames()
        frame = Surface([self.CANVAS_SIZE] * 2, SRCALPHA)
        circle(frame, self.get_color(), [self.CANVAS_SIZE / 2] * 2, self.radius)
        if self.ring:
            frame.blit(self.ring, (0, 0))
        self.add_frame(frame)

    def get_color(self):
        return self.COLORS[Planets.LICKS.index(self.lick)]

    def shrink(self):
        self.radius -= self.SHRINK_STEP
        if self.radius == 0:
            self.get_game().add_obstacles(self.location.center)
            self.get_game().sound_effects.play("planet_eaten-1")
            self.parent.planets.remove(self)
            if len(self.parent.planets) == self.parent.MIN_PLANETS:
                self.get_game().end_game(True)

    def update(self):
        self.add_frames()
        Sprite.update(self)


class HUD(GameChild):

    BACKGROUND_HSLA = 252, 50, 38, 100

    def __init__(self, parent):
        GameChild.__init__(self, parent)
        self.left = Left(self)
        self.life = Life(self)

    def update(self):
        self.left.update()
        self.life.update()


class Left(Animation):

    GRADIENT_HUE_SHIFT = 0
    TEXT_OFFSET = 12, 4
    DIGIT_OFFSET = 61, -5
    DIGIT_HUE_SHIFT = 70

    def __init__(self, parent):
        Animation.__init__(self, parent)
        self.set_background()
        text = self.text = Sprite(self)
        text.load_from_path(self.get_resource("left/text.png"), True)
        text.location.topleft = self.background.location.move(*self.TEXT_OFFSET).topleft
        self.set_digits()

    def set_background(self):
        background = self.background = Sprite(self)
        image = load(self.get_resource("left/background.png")).convert_alpha()
        image.fill(get_hsla_color(*HUD.BACKGROUND_HSLA), None, BLEND_RGBA_MIN)
        background.add_frame(image)
        background.location.topright = self.get_display_surface().get_rect().topright

    def set_digits(self):
        digits = self.digits = []
        for path in sorted(glob(join(self.get_resource("left"), "digit-*.png"))):
            digit = Sprite(self)
            digit.add_frame(get_hue_shifted_surface(load(path).convert_alpha(), self.DIGIT_HUE_SHIFT))
            digit.location.topleft = self.background.location.move(*self.DIGIT_OFFSET).topleft
            digits.append(digit)

    def update(self):
        Animation.update(self)
        self.background.update()
        self.text.update()
        self.digits[len(self.get_game().planets.planets) - Planets.MIN_PLANETS].update()


class Life(GameChild):

    START_POSITION = 11, 6
    MARGIN = 0

    def __init__(self, parent):
        GameChild.__init__(self, parent)
        self.set_background()
        self.set_hearts()

    def set_background(self):
        background = self.background = Sprite(self)
        image = load(self.get_resource("life/background.png")).convert_alpha()
        image.fill(get_hsla_color(*HUD.BACKGROUND_HSLA), None, BLEND_RGBA_MIN)
        background.add_frame(image)
        background.location.bottomleft = self.get_display_surface().get_rect().bottomleft

    def set_hearts(self):
        self.full_heart = load(self.get_resource("life/heart-full.png")).convert_alpha()
        self.empty_heart = load(self.get_resource("life/heart-empty.png")).convert_alpha()

    def update(self):
        self.background.update()
        x, y = self.background.location.move(*self.START_POSITION).topleft
        ds = self.get_display_surface()
        for ii in xrange(Monster.INITAL_LIFE):
            if ii < self.get_game().monster.life:
                ds.blit(self.full_heart, (x, y))
            else:
                ds.blit(self.empty_heart, (x, y))
            x += self.full_heart.get_width()
216.73.216.52
216.73.216.52
216.73.216.52
 
June 29, 2013

A few weeks ago, for Fishing Jam, I made a fishing simulation from what was originally designed to be a time attack arcade game. In the program, Dark Stew, the player controls Aphids, an anthropod who fishes for aquatic creatures living in nine pools of black water.



Fishing means waiting by the pool with the line in. The longer you wait before pulling the line out, the more likely a creature will appear. Aside from walking, it's the only interaction in the game. The creatures are drawings of things you maybe could find underwater in a dream.

The background music is a mix of clips from licensed to share songs on the Free Music Archive. Particularly, Seed64 is an album I used a lot of songs from. The full list of music credits is in the game's README file.

I'm still planning to use the original design in a future version. There would be a reaction-based mini game for catching fish, and the goal would be to catch as many fish as possible within the time limit. I also want to add details and obstacles to the background, which is now a little boring, being a plain, tiled, white floor.

If you want to look at all the drawings or hear the music in the context of the program, there are Windows and source versions available. The source should work on any system with Python and Pygame. If it doesn't, bug reports are much appreciated. Comments are also welcome :)

Dark Stew: Windows, Pygame Source

I wrote in my last post that I would be working on an old prototype about searching a cloud for organisms for Fishing Jam. I decided to wait a while before developing that game, tentatively titled Xenographic Barrier. Its main interactive element is a first-person scope/flashlight, so I'd like to make a Wii version of it.

I'm about to start working on a complete version of Ball & Cup. If I make anything interesting for it, I'll post something. There are a lot of other things I want to write about, like game analyses, my new GP2X and arcades in Korea, and there's still music to release. Lots of fun stuff coming!