from os import environ

from pygame import display, image, mouse
from pygame.locals import *

from GameChild import *

class Display(GameChild):

    def __init__(self, game):
        GameChild.__init__(self, game)
        self.delegate = self.get_delegate()

    def load_configuration(self):
        config = self.get_configuration("display")
        self.centered = config["centered"]
        self.fullscreen_enabled = config["fullscreen"]
        self.caption = config["caption"]
        self.windowed_flag = config["windowed-flag"]
        self.icon_path = self.get_resource("display", "icon-path")
        self.mouse_visibility = self.get_configuration("mouse", "visible")

    def align_window(self):
        if self.centered:
            environ["SDL_VIDEO_CENTERED"] = "1"

    def init_screen(self):
        flags = 0
        if self.fullscreen_requested():
            flags = FULLSCREEN

    def fullscreen_requested(self):
        return not self.check_command_line(self.windowed_flag) and \

    def set_screen(self, flags=0, dimensions=None):
        self.dimensions_changed = dimensions is not None
        if dimensions is None:
            if display.get_surface():
                dimensions = display.get_surface().get_size()
                dimensions = self.get_configuration("display", "dimensions")
        self.screen = display.set_mode(dimensions, flags)
        if self.dimensions_changed:
            interpolator = self.get_game().interpolator
            if interpolator.gui_enabled:

    def set_caption(self):

    def set_icon(self):
        if self.icon_path:
            print self.icon_path

    def set_mouse_visibility(self, visibility=None):
        if visibility is None:
            visibility = self.mouse_visibility
        return mouse.set_visible(visibility)

    def get_screen(self):
        return self.screen

    def get_size(self):
        return self.screen.get_size()

    def toggle_fullscreen(self, event):
        if, "toggle-fullscreen"):
            screen = self.screen
            cpy = screen.convert()
            self.set_screen(self.screen.get_flags() ^ FULLSCREEN)
            screen.blit(cpy, (0, 0))
from os.path import exists, join, basename, normpath, abspath
from sys import argv

from pygame import mixer, event, time
from pygame.locals import *

import Game

class GameChild:

    def __init__(self, parent=None):
        self.parent = parent = self.get_game()

    def get_game(self):
        current = self
        while not isinstance(current, Game.Game):
            current = current.parent
        return current

    def get_configuration(self, section=None, option=None):
        config =
        if option and section:
            return config.get(section, option)
        if section:
            return config.get_section(section)
        return config

    def get_input(self):

    def get_screen(self):

    def get_display_surface(self):
        current = self
        attribute = "display_surface"
        while not isinstance(current, Game.Game):
            if hasattr(current, attribute):
                return getattr(current, attribute)
            current = current.parent
        return current.display.get_screen()

    def get_audio(self):

    def get_delegate(self):

    def get_resource(self, path_or_section, option=None):
        config = self.get_configuration()
        rel_path = path_or_section
        if option is not None:
            rel_path = config.get(path_or_section, option)
        if rel_path:
            for root in config.get("setup", "resource-search-path"):
                if self.is_shared_mode() and not self.is_absolute_path(root):
                path = join(root, rel_path)
                if exists(path):
                    return path
        self.print_debug("Couldn't find resource: {0} {1}".\
                                   format(path_or_section, option))

    def is_shared_mode(self):
        return self.check_command_line("s")

    def check_command_line(self, flag):
        return "-" + flag in argv

    def print_debug(self, statement):
        if self.is_debug_mode():
            print statement

    def is_debug_mode(self):
        return self.check_command_line("d")

    def is_absolute_path(self, path):
        return normpath(path) == abspath(path)

    def subscribe(self, callback, kind=None):, kind)

    def unsubscribe(self, callback, kind=None):, kind)
import cProfile
from time import strftime
from os import mkdir
from os.path import join, exists

from GameChild import GameChild

class Profile(cProfile.Profile, GameChild):

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

    def requested(self):
        return self.check_command_line("p")

    def end(self):
        if self.requested():
            root = "stat/"
            if not exists(root):
            self.dump_stats(join(root, strftime("%Y%m%d-%H%M_%S.stat")))
from GameChild import GameChild

class Animation(GameChild):

    def __init__(self, parent, method=None, interval=None, unfiltered=False):
        GameChild.__init__(self, parent)
        self.unfiltered = unfiltered
        self.default_method = method or self.build_frame
        self.accounts = {}
        self.register(self.default_method, interval=interval)
        self.last_update = 0

    def build_frame(self):

    def register(self, *args, **kwargs):
        interval = None
        if kwargs.has_key("interval"):
            interval = kwargs["interval"]
        for method in args:
            if method not in self.accounts:
                self.accounts[method] = Account(interval, self)

    def play(self, method=None, interval=None, delay=0, play_once=False,
        account = self.accounts[self.get_default(method)]
        if interval:

    def get_default(self, method=None):
        if not method:
            method = self.default_method
        return method

    def halt(self, method=None):
        if not method:
            for account in self.accounts.values():
            if self.accounts.has_key(method):

    def is_playing(self, method=None, check_all=False, include_delay=False):
        if check_all:
            return any(self.is_account_playing(account, include_delay) for \
                       method, account in self.accounts.iteritems())
        return self.is_account_playing(self.accounts[self.get_default(method)],

    def is_account_playing(self, account, include_delay):
        return account.playing and (not include_delay or not account.delay)

    def update(self):
        for method, account in self.accounts.iteritems():
            if account.update():

class Account:

    def __init__(self, interval, animation):
        self.animation = animation
        self.time_filter = animation.get_game().time_filter
        self.interval_index = 0
        self.last_frame = 0

    def set_interval(self, interval):
        if isinstance(interval, int) or isinstance(interval, str):
            interval = [interval]
        self.interval = interval

    def set_delay(self, delay):
        self.delay = delay

    def set_play_once(self, play_once):
        self.play_once = play_once

    def set_args(self, args):
        self.args = args

    def play(self):
        self.playing = True

    def halt(self):
        self.last_update = None
        self.playing = False

    def update(self):
        if self.playing:
            if self.animation.unfiltered:
                ticks = self.time_filter.get_unfiltered_ticks()
                ticks = self.time_filter.get_ticks()
            if not self.delay:
                interval = self.interval
                if interval:
                    if ticks - self.last_frame < self.get_current_interval():
                        return False
                    self.last_frame = ticks
                if self.play_once:
                return True

    def get_current_interval(self):
        return self.interval[self.interval_index]

    def increment_interval_index(self):
        index = self.interval_index + 1
        if index >= len(self.interval):
            index = 0
        self.interval_index = index

    def update_delay(self, ticks):
        delay = self.delay
        if delay > 0:
            last_update = self.last_update or ticks
            delay -= ticks - last_update
            if delay < 0:
                delay = 0
        self.last_update = ticks
        self.delay = delay
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!

↠ RSS Feed ↞