Archive

Archives pour 07/2009

SIGGRAPH

Source: Powder News

Friday July 31st, 2009 Well, I'm off to New Orleans for SIGGRAPH '09 the coming Monday. Unfortunately, it has been a while since the latest version so I'll have to use this as an excuse for any delay. Any also at Siggraph are welcome to send me a message. My schedule can be rather hectic, but I can always find time for POWDER players. If you are wondering how you can contact me, I recommend reading to the end of the README.TXT that ships with POWDER. -- Delivered by Feed43 service
Categories: Planet Roguelike, Powder News Tags:

Kamyran’s Eye 2 – news 2009-07-31 03:03:00

Source: Kamyran's Eye 2 - news

A beta version of the MacOS port is now available.

Dungeon Crawl Stone Soup 0.5.1 released!

Source: Dungeon Crawl Stone Soup

Stone Soup 0.5.1 has been released, fixing an abundance of bugs. The source code and Windows binaries are already available for download, but Mac [...]

Kamyran’s Eye 2 – news 2009-07-30 00:54:00

Source: Kamyran's Eye 2 - news

another bugfix release candidate

Message repeat bug fixed

Source: Kaduria

This was one of the bugs that was around for a long time, because the lack of consistent thinking. But I have become better in that too, so I hope it's now fixed. The same bug is also present in Teemu, but it's not a crashing one. The routine just doesn't show repeating messages properly.

The thing with difficult bugs and features is that you have to fix them some day and it's really that hard to fix stuff that increases the time spent on a game project. If only everything was easy!
Categories: Kaduria, Planet Roguelike Tags:

Column: @Play: The Python Strikes! You Are Being Squeezed!

Source: GameSetWatch Column @Play

Roguelike column thumbnail ['@ Play' is a monthly column by John Harris which discusses the history, present and future of the Roguelike dungeon exploring genre. This time - a discussion of using Python to make Roguelike games.] So let's talk a bit about Roguelike development languages. Traditionally, the One True Roguelike Language has been C. All of the current "major roguelikes," Nethack, Angband, ADOM and Dungeon Crawl, are either written in it or in C++. (Nethack makes use of bison and lex, and Angband used to use Lua for scripting.) The genre's origins on Unix systems, its ties with the curses console library, its reliance on a console in general, and the fact that when the genre got started C was basically it as far as serious programming languages go, all these things combined to identify the genre somewhat strongly with C. This is not as much the case now. As this year's 7DRL competition demonstrated, new roguelikes are now written in all kinds of languages, ranging from Java to MOS6510 assembly. A few were even written in Python, a language that has historically been regarded as more of a scripting language. I mean, scoff scoff, what business does a "real game" have being written in something like Python?

Continue reading "Column: @Play: The Python Strikes! You Are Being Squeezed!" »

-- Delivered by Feed43 service

stages (a roguelike in Python # 14)

Source: Kooneiform » roguelike

A few days ago I got to the point where I wanted to add switching levels and a get command. Adding the get command led me to consider how I would represent inventory in the code, and I thought simple, every thing has a list of things it holds.

The new code is after the jump, but first some explanation.

My thought was that a level is simply a thing that holds the things inside it. OK, I was thinking this is good, because I had wanted to have different kinds of levels in the game, like airships, giant woolly mammoths and so forth, and this would potentially make it straightforward to have a giant woolly mammoth walking around that the player could jump on and explore. So I decided a level would be a kind of handler that gets attached to a thing and adds the functionality of a map.

As you can tell I’m not too concerned with representing scale at this point — I don’t think I will be at any point really, this is a game after all. Regardless, I decided to call a level handler a Stage.

You can see in previous postings of the demo that I managed the map in a single World object. So I needed to do some heavy rewriting, and this led me to reconsider the handler/thing structure altogether. As a result I threw out the Brain class and now add handlers directly to a Thing. I think this is simpler overall.

At one point I got sidetracked by the magic of Python’s __getattr__. This is an attribute on every object, that’s executed when the interpreter doesn’t find the attribute it’s looking for as a result of a call on a property or function. So I was doing things like calling thing.foo where foo was actually an attribute on a handler, not the thing itself. However this proved to be more trouble than it was worth (since, as far as I could tell, __getattr__ expects you to return a value when called, and I decided this was too much hassle). The easier solution for me was to rewrite things so that wherever I call an attribute of a handler, I access it through a dictionary of handlers on the thing, like this:

            cells = stage.handlers['Stage'].cells_get(self.thing, self.fov_radius)

A little verbose, but easier in the end, and maybe clearer overall.

Saving the game now means saving the stages in a game. I’ll need to revise this in the future to account for stages within stages though. Switching stages means removing the thing from its current stage and adding it to the new stage. By keeping track of the player’s current stage I know what to draw to the screen. Currently I only update things in the player’s stage, so this will need to be changed in the future so time doesn’t freeze on a stage when the player leaves it. And finally, getting and dropping things means removing and adding the thing to the things that are getting it or receiving it.

There’s a crude inventory display in the upper part of the UI at the moment, but there’s only one thing to get.

Things are now kept in a file by stage, which is named like stage_name.things, and the maps are in stage_name.map.

addendum: With all this talk of stages, it may seem as if something is being left out — namely procedural generation of levels! And for the moment this is true, but as I think I’ve mentioned before I’m not going to focus on generating levels until I have a better idea of what I want the game to be. So for now I’m sticking with hardcoded content to give me a base to test on.

#!/usr/bin/python

'''
libtcod OO python tutorial
This code modifies samples_py.py from libtcod 1.4.1. It shows a '@'
walking around a scrolling map with a source of light giving simple FOV.
It's in the public domain.
'''

#############################################
# imports
#############################################

import os
import random
import yaml
import cProfile
import libtcod.libtcodpy as libtcod

#############################################
# profile
#############################################

PROFILER = cProfile.Profile()

#############################################
# constants
#############################################

PROFILE = False
SAVING = True

#############################################
# thing
#############################################

class Thing(object):
    def __init__(self, name, x, y, speed, properties, *handlers):
        self.name = name
        self.x = x
        self.y = y
        self.speed = speed
        self.properties = properties
        self.handlers = {}
        if not None in handlers:
            for string in handlers:
                handler = eval(string)
                self.handlers[string] = handler(self)

        self.things = []
        self.stage = None

    def set_position(self, dx, dy):
        self.x = self.x + dx
        self.y = self.y + dy

    def set_name(self, name):
        self.name = name

#############################################
# handlers
#############################################

class Stage(object):
    def __init__(self, thing):
        self.thing = thing
        self.cell_types = {
                            ' ' : ['ground', 'transparent', 'walkable'],
                            '#' : ['wall']}
        self.width = None
        self.height = None
        self.state = []

        name = self.thing.name + '.map'
        self.load(name)

    def load(self, name):
        file = open(name, 'r')
        for line in file:
            line = line.strip(' \n')
            line_data = []
            for cell in line:
                line_data.append(self.cell_types[cell])
            self.state.append(line_data)

        self.width = len(self.state[0])
        self.height = len(self.state)

        file.close()

    def update(self):
        pass

    def cells_get(self, thing=None, radius=None):
        cells = []

        r = 0
        if radius:
            r = radius

        y_start = 0
        x_start = 0
        y_end = self.height
        x_end = self.width
        if thing:
            y_start = max(thing.y - r, 0)
            x_start = max(thing.x - r, 0)
            y_end = min(self.height, thing.y + r + 1)
            x_end = min(self.width, thing.x + r + 1)

        for y in range(y_start, y_end):
            for x in range(x_start, x_end):
                cells.append((x, y))

        return cells

    def cell_has_monster(self, x, y):
        for thing in self.thing.things:
            if (thing.x, thing.y) == (x, y) and (thing.name == 'monster' or thing.name == 'player'):
                return True

    def cell_is_unwalkable(self, x, y):
        if not 'walkable' in self.state[y][x]:
            return True

    def cell_has_closed_door(self, x, y):
        for thing in self.thing.things:
            if (thing.x, thing.y) == (x, y) and thing.name == 'closed door':
                return True

    def cell_type_get(self, x, y):
        return self.state[y][x][0]

    def cell_add_property(self, x, y, property):
        self.state[y][x] = self.state[y][x] + [property]

#############################################
# do things
#############################################

class Fov(object):
    def __init__(self, thing):
        self.thing = thing

        self.fov_map = None
        self.fov_radius = 2
        self.fov_map_to_world_coordinates = []

    def update(self):
        stage = self.thing.stage

        if stage == player.thing.stage:
            self.fov_map_to_world_coordinates = []
            cells = stage.handlers['Stage'].cells_get(self.thing, self.fov_radius)
            cell_first = cells[0]
            cell_last = cells[len(cells)-1]

            difference_right = cell_last[0] - self.thing.x
            difference_bottom = cell_last[1] - self.thing.y
            difference_left = self.thing.x - cell_first[0]
            difference_top = self.thing.y - cell_first[1]

            fov_map_size_x = difference_right + difference_left + 1
            fov_map_size_y = difference_bottom + difference_top + 1

            self.fov_map = libtcod.map_new(fov_map_size_x, fov_map_size_y)

            for i in range(len(cells)):
                x, y = cells[i]
                fov_map_x = i % fov_map_size_x
                fov_map_y = i // fov_map_size_x
                cell = stage.handlers['Stage'].state[y][x]
                if 'walkable' in cell and 'transparent' in cell:
                    libtcod.map_set_properties(self.fov_map, fov_map_x, fov_map_y, True, True)
                elif 'walkable' in cell:
                    libtcod.map_set_properties(self.fov_map, fov_map_x, fov_map_y, False, True)
                elif 'transparent' in cell:
                    libtcod.map_set_properties(self.fov_map, fov_map_x, fov_map_y, True, False)

                for thing in self.thing.stage.things:
                    if thing.name == 'window' and (x, y) == (thing.x, thing.y):
                        libtcod.map_set_properties(self.fov_map, fov_map_x, fov_map_y, True, False)

                    if thing.name == 'closed door' and (x, y) == (thing.x, thing.y):
                        libtcod.map_set_properties(self.fov_map, fov_map_x, fov_map_y, False, False)

                if (x, y) == (self.thing.x, self.thing.y):
                    fov_x_focus = fov_map_x
                    fov_y_focus = fov_map_y

                self.fov_map_to_world_coordinates.append(((fov_map_x, fov_map_y), (x, y)))

            libtcod.map_compute_fov(self.fov_map, fov_x_focus, fov_y_focus, self.fov_radius, True)

            if self.thing.name == 'player':
                for i in range(len(cells)):
                    x, y = cells[i]
                    fov_map_x = i % fov_map_size_x
                    fov_map_y = i // fov_map_size_x
                    cell = stage.handlers['Stage'].state[y][x]
                    if (libtcod.map_is_in_fov(self.fov_map, fov_map_x, fov_map_y) and
                        not 'discovered' in stage.handlers['Stage'].state[y][x]):
                        stage.handlers['Stage'].cell_add_property(x, y, 'discovered')

class AI(object):
    def __init__(self, thing):
        self.thing = thing
        self.choices = ['north', 'south', 'west', 'east']

    def update(self):
        if self.thing.stage == player.thing.stage:
            choice = random.randint(0, 3)
            command_name = self.choices[choice]
            game.command.do(command_name, self.thing)

#############################################
# player
#############################################

class Awesome(object):
    def __init__(self):
        self.ui = UI()
        self.view = View()
        self.input = Input()
        self.name = ''
        self.thing = None

    def create(self, stage):
        self.thing = Thing('player', 26, 15, 'normal', '', 'Fov')
        stage.things.append(self.thing)
        self.thing.stage = stage
        self.thing.handlers['Fov'].fov_radius = 5

class UI(object):
    def __init__(self):
        self.width = 55
        self.height = 30
        self.state = self.splash

        self.dialog = libtcod.console_new(30, 10)
        libtcod.console_set_background_color(self.dialog, libtcod.light_grey)
        libtcod.console_set_foreground_color(self.dialog, libtcod.black)
        libtcod.console_clear(self.dialog)

        self.top = libtcod.console_new(53, 3)
        libtcod.console_set_background_color(self.top, libtcod.light_grey)
        libtcod.console_set_foreground_color(self.top, libtcod.white)
        libtcod.console_clear(self.top)

        self.bottom = libtcod.console_new(53, 3)
        libtcod.console_set_background_color(self.bottom, libtcod.light_grey)
        libtcod.console_set_foreground_color(self.bottom, libtcod.white)
        libtcod.console_clear(self.bottom)

        self.right = libtcod.console_new(21, 20)
        libtcod.console_set_background_color(self.right, libtcod.grey)
        libtcod.console_set_foreground_color(self.right, libtcod.white)
        libtcod.console_clear(self.right)

        self.main_menu_options = ['new game', 'load saved game']
        self.main_menu_cursor = 0
        self.load_game_selected = ''
        self.load_game_cursor = 0
        self.save_games = []

        self.chars = {
                        'player'    : '@',
                        'monster'   : '?',
                        'ground'    : ' ',
                        'wall'      : '#',
                        'window'    : libtcod.CHAR_DHLINE,
                        'lamp'      : 'l',
                        'knife'     : 'k',
                        'open door' : '-',
                        'closed door'   : '+',
                        'portal'    : 'O'}

        self.cell_colors = {
                            'wall' : libtcod.Color(67, 65, 52),
                            'ground' : libtcod.Color(145, 140, 140),
                            'dark': libtcod.Color(1, 1, 1),
                            'lit': libtcod.Color(255, 255, 33),
                            'visible': libtcod.Color(254, 254, 233),
                            'discovered': libtcod.Color(50, 50, 100)}    

        font = os.path.join('fonts', 'arial12x12.png')
        libtcod.console_set_custom_font(
                                        font,
                                        libtcod.FONT_LAYOUT_TCOD |
                                        libtcod.FONT_TYPE_GREYSCALE,
                                        32,
                                        8) 

        libtcod.console_init_root(
                                    self.width,
                                    self.height,
                                    'Python Demo',
                                    False)
        libtcod.console_set_background_color(0, libtcod.dark_grey)
        libtcod.mouse_show_cursor(False)
        libtcod.console_credits()

        self.states = {
                        'splash'    : self.splash,
                        'main_menu' : self.main_menu,
                        'new_game'  : self.new_game,
                        'load_game' : self.load_game,
                        'gameplay'  : self.gameplay}

    def change_state(self, state):
        self.state = self.states[state]

    def draw(self):
        self.state()

    def splash(self):
        libtcod.console_clear(0)
        libtcod.console_set_foreground_color(0, libtcod.white)
        libtcod.console_print_center(0, self.width//2, self.height//2-4, libtcod.BKGND_NONE, "A Python Demo")
        libtcod.console_flush()

    def main_menu(self):
        libtcod.console_clear(0)
        libtcod.console_set_foreground_color(0, libtcod.white)
        libtcod.console_print_center(0, self.width//2, self.height//2-4, libtcod.BKGND_NONE, "A Python Demo")

        for i in range(len(self.main_menu_options)):
            option = self.main_menu_options[i]
            if i == self.main_menu_cursor:
                cursor = '>'
            else:
                cursor = ' '
            libtcod.console_print_left(0, 20, self.height//2+i, libtcod.BKGND_NONE, "%c%s %s%c" % (libtcod.COLCTRL_1, cursor, option, libtcod.COLCTRL_STOP))

        libtcod.console_flush()

    def new_game(self):
        libtcod.console_clear(0)
        libtcod.console_clear(self.dialog)
        libtcod.console_print_left(self.dialog, 2, 5, libtcod.BKGND_NONE, "%cTell me your name, hunter. %c" % (libtcod.COLCTRL_1, libtcod.COLCTRL_STOP))
        libtcod.console_print_center(self.dialog, 15, 8, libtcod.BKGND_NONE, "%c%s_%c" % (libtcod.COLCTRL_1, player.name, libtcod.COLCTRL_STOP))

        libtcod.console_blit(
                                self.dialog,
                                0,
                                0,
                                30,
                                10,
                                0,
                                (self.width - 30)/2,
                                10,
                                200)
        libtcod.console_flush()

    def load_game(self):
        libtcod.console_clear(0)
        libtcod.console_clear(self.dialog)
        libtcod.console_print_center(self.dialog, 15, 1, libtcod.BKGND_NONE, "%cTell me your name, hunter. %c" % (libtcod.COLCTRL_1, libtcod.COLCTRL_STOP))

        i = self.load_game_cursor
        game = self.save_games[i].split('.')[0].replace('_', ' ')

        libtcod.console_print_center(self.dialog, 15, 4, libtcod.BKGND_NONE, "%c> %s%c" % (libtcod.COLCTRL_1, game, libtcod.COLCTRL_STOP))
        libtcod.console_print_center(self.dialog, 15, 8, libtcod.BKGND_NONE, "%cup/down to select%c" % (libtcod.COLCTRL_1, libtcod.COLCTRL_STOP))

        libtcod.console_blit(
                                self.dialog,
                                0,
                                0,
                                30,
                                10,
                                0,
                                (self.width - 30)/2,
                                10,
                                200)
        libtcod.console_flush()

    def gameplay(self):
        libtcod.console_clear(0)
        libtcod.console_clear(self.right)
        libtcod.console_clear(self.top)

        libtcod.console_print_left(self.right, 1, 1, libtcod.BKGND_NONE, "HJKL move around")
        libtcod.console_print_left(self.right, 1, 4, libtcod.BKGND_NONE, "%s" % player.name)
        libtcod.console_print_left(self.right, 1, 5, libtcod.BKGND_NONE, "in %s" % player.thing.stage.name)

        libtcod.console_print_left(self.right, 1, 7, libtcod.BKGND_NONE, "game turn: %d" % game.time.turns)
        libtcod.console_print_left(self.right, 1, 9, libtcod.BKGND_NONE, "%s" % game.message.text)

        libtcod.console_print_left(self.top, 1, 1, libtcod.BKGND_NONE, "%s" % player.thing.things)

        libtcod.console_blit(
                                player.view.screen,
                                0,
                                0,
                                player.view.width,
                                player.view.height,
                                0,
                                1,
                                5,
                                255)

        libtcod.console_blit(
                                self.top,
                                0,
                                0,
                                53,
                                3,
                                0,
                                1,
                                1,
                                255)

        libtcod.console_blit(
                                self.bottom,
                                0,
                                0,
                                53,
                                3,
                                0,
                                1,
                                26,
                                255)

        libtcod.console_blit(
                                self.right,
                                0,
                                0,
                                21,
                                20,
                                0,
                                33,
                                5,
                                255)
        libtcod.console_flush()

class View(object):
    def __init__(self):
        self.width = 30
        self.height = 20
        self.anchor_x = None
        self.anchor_y = None
        self.screen = libtcod.console_new(self.width, self.height)
        libtcod.console_set_foreground_color(self.screen, libtcod.black)

    def update(self):
        stage = player.thing.stage.handlers['Stage']

        libtcod.console_clear(self.screen)

        self.anchor_x = min(max(0, player.thing.x - self.width//2), stage.width - self.width)
        self.anchor_y = min(max(0, player.thing.y - self.height//2), stage.height - self.height)

        viewport = []
        for j in range(self.height):
            line = []
            for i in range(self.width):
                x, y = self.viewport_ij_to_world_xy(i, j)
                cell = {
                        'light'   : 0,
                        'char'    : ' ',
                        'fov'     : [],
                        'cell_type'     : stage.cell_type_get(x, y),
                        'discovered'    : 0,
                        'scalar'        : 1}

                if 'discovered' in stage.state[y][x]:
                    cell['discovered'] = 1

                line.append(cell)
            viewport.append(line)

        maps = []
        for thing in player.thing.stage.things:
            if 'Fov' in thing.handlers:
                maps.append((thing, thing.handlers['Fov'].fov_map, thing.handlers['Fov'].fov_map_to_world_coordinates))

        for tuple in maps:
            thing, fov_map, fov_map_to_world_coordinates = tuple
            for cell in fov_map_to_world_coordinates:
                x, y = cell[1][0], cell[1][1]

                if self.xy_in_viewport(x, y):
                    fov_map_x, fov_map_y = cell[0][0], cell[0][1]
                    i, j = self.world_xy_to_viewport_ij(x, y)

                    distance_squared = max(1, (x - thing.x) * (x - thing.x) + (y - thing.y) * (y - thing.y))
                    fov_radius_squared = thing.handlers['Fov'].fov_radius * thing.handlers['Fov'].fov_radius
                    scalar = (fov_radius_squared - distance_squared * 1.0)/fov_radius_squared

                    if libtcod.map_is_in_fov(fov_map, fov_map_x, fov_map_y):
                        viewport[j][i]['fov'].append(thing.name)

                        if thing.name == 'lamp':
                            viewport[j][i]['light'] = max(.5, scalar)

                        if thing.name == 'player':
                            viewport[j][i]['scalar'] = max(.5, scalar)

        for j in range(self.height):
            for i in range(self.width):
                cell = viewport[j][i]
                affect = 'dark'
                cell_type = cell['cell_type']
                if 'player' in cell['fov']:
                    if cell['light'] == 0:
                        affect = 'visible'
                    else:
                        affect = 'lit'

                        if cell_type == 'wall':
                            n = max(0, j-1)
                            e = min(self.width-1, i+1)
                            s = min(self.height-1, j+1)
                            w = max(0, i-1)
                            cells = [
                                        viewport[n][w],
                                        viewport[n][i],
                                        viewport[n][e],
                                        viewport[j][w],
                                        viewport[j][e],
                                        viewport[s][w],
                                        viewport[s][i],
                                        viewport[s][e]]
                            for adjacent_cell in cells:
                                if not 'player' in adjacent_cell['fov'] and adjacent_cell['cell_type'] == 'ground' and adjacent_cell['light'] > 0:
                                    affect = 'visible'
                                    break
                        else:
                            cell['scalar'] = cell['light']

                elif cell['discovered'] > 0:
                    affect = 'discovered'

                if 'player' in cell['fov']:
                    affect_color = player.ui.cell_colors[affect] * cell['scalar']
                else:
                    affect_color = player.ui.cell_colors[affect]
                cell_type_color = player.ui.cell_colors[cell_type]
                color = affect_color * cell_type_color
                char = cell['char']
                libtcod.console_set_back(self.screen, i, j, color, libtcod.BKGND_SET)
                libtcod.console_put_char(self.screen, i, j, char, libtcod.BKGND_NONE)                        

        for thing in player.thing.stage.things:
            i, j = self.world_xy_to_viewport_ij(thing.x, thing.y)
            if self.xy_in_viewport(thing.x, thing.y) and 'player' in viewport[j][i]['fov']:
                libtcod.console_put_char(self.screen, i, j, player.ui.chars[thing.name], libtcod.BKGND_NONE)

    def world_xy_to_viewport_ij(self, x, y):
        i = x - self.anchor_x
        j = y - self.anchor_y
        return (i, j)

    def viewport_ij_to_world_xy(self, i, j):
        x = i + self.anchor_x
        y = j + self.anchor_y
        return (x, y)

    def xy_in_viewport(self, x, y):
        if (self.anchor_x <= x < self.anchor_x+self.width and
            self.anchor_y <= y < self.anchor_y+self.height):
                return True

class Input(object):
    def __init__(self):
        self.state = self.splash

        file = open('keys.cfg', 'r')
        self.keycfg = yaml.load(file)

        self._keys = {
                        'k' : 'k',
                        'j' : 'j',
                        'h' : 'h',
                        'l' : 'l',
                        'p' : 'p',
                        libtcod.KEY_UP : 'up arrow',
                        libtcod.KEY_RIGHT : 'right arrow',
                        libtcod.KEY_LEFT : 'left arrow',
                        libtcod.KEY_DOWN : 'down arrow',
                        libtcod.KEY_KP8 : 'keypad 8',
                        libtcod.KEY_KP6 : 'keypad 6',
                        libtcod.KEY_KP4 : 'keypad 4',
                        libtcod.KEY_KP2 : 'keypad 2',
                        'x' : 'x',
                        libtcod.KEY_SPACE : 'space bar'}

        self.ctrl = ''

        self.states = {
                        'splash'    : self.splash,
                        'main_menu' : self.main_menu,
                        'new_game'  : self.new_game,
                        'load_game' : self.load_game,
                        'gameplay'  : self.gameplay}

    def get_key(self, key):
        if key.lctrl:
            self.ctrl = 'ctrl '
        else:
            self.ctrl = ''

        if not key.vk == 65:
            return key.vk
        else:
            return chr(key.c)

    def change_state(self, state):
        self.state = self.states[state]

    def update(self):
        self.state()

    def splash(self):
        libtcod.console_wait_for_keypress(True)

    def main_menu(self):
        self.key = libtcod.console_wait_for_keypress(True)

        if self.key.vk == libtcod.KEY_ESCAPE or libtcod.console_is_window_closed():
            game.exit = True
        elif self.key.vk == libtcod.KEY_ENTER:
            pass
        elif self.key.vk == libtcod.KEY_DOWN:
            player.ui.main_menu_cursor = min(len(player.ui.main_menu_options)-1, player.ui.main_menu_cursor+1)
        elif self.key.vk == libtcod.KEY_UP:
            player.ui.main_menu_cursor = max(0, player.ui.main_menu_cursor-1)

    def new_game(self):
        self.key = libtcod.console_wait_for_keypress(True)
        if self.key.vk == libtcod.KEY_ENTER:
            pass
        elif self.key.vk == libtcod.KEY_BACKSPACE:
            name = player.name
            player.name = name[:-1]
        elif self.key.vk in (65, libtcod.KEY_SPACE):
            player.name = player.name + chr(self.key.c)

    def load_game(self):
        self.key = libtcod.console_wait_for_keypress(True)
        if self.key.vk == libtcod.KEY_ENTER or self.key.vk == libtcod.KEY_ESCAPE:
            pass
        elif self.key.vk == libtcod.KEY_DOWN:
            player.ui.load_game_cursor = min(len(player.ui.save_games)-1, player.ui.load_game_cursor+1)
        elif self.key.vk == libtcod.KEY_UP:
            player.ui.load_game_cursor = max(0, player.ui.load_game_cursor-1)

    def gameplay(self):
        self.key = libtcod.console_wait_for_keypress(True)
        key = self.get_key(self.key)
        if key in self._keys:
            key = self._keys[key]

        while not key in self.keycfg:
            if self.key.vk == libtcod.KEY_ESCAPE or libtcod.console_is_window_closed():
                if SAVING: game.save()
                game.exit = True
                return
            game.message.text = "Try again!"
            player.ui.draw()
            self.key = libtcod.console_wait_for_keypress(True)
            key = self.get_key(self.key)
            if key in self._keys:
                key = self._keys[key]

        command_name = self.ctrl + self.keycfg[key]
        game.message.text = "Go!"

        if game.command.do(command_name, player.thing) == 'free action':
            player.view.update()
            player.ui.draw()
            self.gameplay()

#############################################
# game
#############################################
class Game(object):
    def __init__(self):
        self.message = Message()
        self.time = Time()
        self.command = Command()

        self.state = self.splash
        self.exit = False

        self.stages = []

        self.states = {
                        'splash'    : self.splash,
                        'main_menu' : self.main_menu,
                        'new_game'  : self.new_game,
                        'load_game' : self.load_game,
                        'gameplay'  : self.gameplay}

    def change_state(self, state):
        self.state = self.states[state]
        player.input.change_state(state)
        player.ui.change_state(state)

    def update(self):
        self.state()

    def splash(self):
        player.ui.draw()
        player.input.update()

        self.change_state('main_menu')

    def main_menu(self):
        if not player.ui.save_games:
            dir = os.listdir('.')
            for file in dir:
                if 'sav' in file:
                    player.ui.save_games.append(file)

        player.ui.draw()
        player.input.update()

        if player.input.key.vk == libtcod.KEY_ENTER:
            if player.ui.main_menu_options[player.ui.main_menu_cursor] == 'new game':
                self.change_state('new_game')

            elif player.ui.main_menu_options[player.ui.main_menu_cursor] == 'load saved game' and player.ui.save_games:
                self.change_state('load_game')

    def new_game(self):
        player.ui.draw()
        player.input.update()

        if player.input.key.vk == libtcod.KEY_ENTER:
            if player.ui.main_menu_options[player.ui.main_menu_cursor] == 'new game':
                things_files = []
                dir = os.listdir('.')
                for file in dir:
                    if 'things' in file:
                        things_files.append(file)

                for file in things_files:
                    new_things = []
                    file = open(file, 'r')
                    things = yaml.load_all(file)
                    for data in things:
                        thing = Thing(*data)
                        new_things.append(thing)
                    file.close()

                    _stage = None
                    for thing in new_things:
                        if 'Stage' in thing.handlers:
                            _stage = thing
                            self.stages.append(_stage)

                    for thing in new_things:
                        if not 'Stage' in thing.handlers:
                            thing.stage = _stage
                            _stage.things.append(thing)

                current_stage = None
                for stage in self.stages:
                    if stage.name == 'Cyanothus':
                        current_stage = stage

                player.create(current_stage)

                for handler in player.thing.handlers:
                    player.thing.handlers[handler].update()
                player.view.update()

                self.change_state('gameplay')

        elif player.input.key.vk == libtcod.KEY_ESCAPE:
            self.change_state('main_menu')

    def load_game(self):
        player.ui.draw()
        player.input.update()

        if player.input.key.vk == libtcod.KEY_ENTER:
            file = player.ui.save_games[player.ui.load_game_cursor]
            self.load(file)

            player.name = file.split('.')[0].replace('_', ' ')

            self.change_state('gameplay')

        elif player.input.key.vk == libtcod.KEY_ESCAPE:
            self.change_state('main_menu')

    def gameplay(self):
        if self.time.phase in self.time.phases_for[player.thing.speed]:
            player.input.update()

        if game.exit == True:
            return

        for thing in player.thing.stage.things:
            if self.time.phase in self.time.phases_for[thing.speed]:
                for handler in thing.handlers:
                    thing.handlers[handler].update()      

        self.time.tick()

        if PROFILE:
            PROFILER.runcall(player.view.update)
            PROFILER.dump_stats('profile_view_draw')
        else:
            player.view.update()

    def save(self):
        for stage in self.stages:
            for thing in stage.things:
                if 'Fov' in thing.handlers:
                    thing.handlers['Fov'].fov_map_to_world_coordinates = []
                if hasattr(thing, 'stage'):
                    thing.stage = None

        name = player.name.replace(' ', '_') + '.sav'
        file = open(name, 'w')
        yaml.dump_all(self.stages, file)
        file.close()

    def load(self, name):
        file = open(name, 'r')
        self.stages = list(yaml.load_all(file))
        file.close()

        for stage in self.stages:
            for thing in stage.things:
                if thing.name == 'player':
                    player.thing = thing

        for stage in self.stages:
            for thing in stage.things:
                thing.stage = stage

        for handler in player.thing.handlers:
            player.thing.handlers[handler].update()
        player.view.update()

class Message(object):
    def __init__(self):
        self.text = "Go!"

class Time(object):
    def __init__(self):
        self.phases = ['fast', 'normal', 'slow', 'quick', 'normal']
        self.phases_for = {
                            'fast' : ('fast', 'normal', 'slow'),
                            'normal' : ('normal', 'slow'),
                            'slow' : ('normal'),
                            'quick' : ('normal', 'slow', 'quick'),
                            'fast+quick' : ('fast', 'normal', 'slow', 'quick'),
                            'fast+slow' : ('fast', 'normal'),
                            'quick+slow' : ('quick', 'normal'),
                            'fast+quick+slow' : ('fast', 'normal', 'quick')}

        self.phase = self.phases[1]
        self.phase_count = 0
        self.turns = 0

    def tick(self):
        self.phase_count += 1
        self.phase = self.phases[self.phase_count % 5]
        if self.phase in self.phases_for['normal']: self.turns += 1

class Command(object):
    def __init__(self):
        self.playbook = {
                        'north' : ('takes turn', self.move, 0, -1),
                        'east'  : ('takes turn', self.move, 1, 0),
                        'south' : ('takes turn', self.move, 0, 1),
                        'west'  : ('takes turn', self.move, -1, 0),
                        'ctrl north'    : ('takes turn', self.ctrl, 0, -1),
                        'ctrl east'     : ('takes turn', self.ctrl, 1, 0),
                        'ctrl south'    : ('takes turn', self.ctrl, 0, 1),
                        'ctrl west'     : ('takes turn', self.ctrl, -1, 0),
                        'direct'   : ('takes turn', self.direct, None),
                        'indirect'  : ('free action', self.indirect, None),
                        'print history' : ('free action', self.print_history, None)}

        self.rulebook = {
                        'move' : ('cell has monster', 'cell is unwalkable', 'cell has closed door', 'open door implicitly'),
                        'close_door' : ('cell has closed door',),
                        'get'   : ('thing not gettable',),
                        'drop'  : ()}

        self.rules = {
                        # condition checks

                        'cell has monster' : "enactor.stage.handlers['Stage'].cell_has_monster(enactor.x+dx, enactor.y+dy)",
                        'cell is unwalkable' : "enactor.stage.handlers['Stage'].cell_is_unwalkable(enactor.x+dx, enactor.y+dy)",
                        'cell has closed door' : "enactor.stage.handlers['Stage'].cell_has_closed_door(enactor.x+dx, enactor.y+dy)",
                        'thing not gettable' : "not 'gettable' in target.properties",

                        # automatic procedures

                        'open door implicitly' : "self.open_door_implicitly(enactor.x+dx, enactor.y+dy)"}

        self.history = []

    def do(self, string, enactor):
        command = self.playbook[string]
        command_name, args = command[1], command[2:]
        command_name(enactor, *args)
        self.history.append((string, enactor.name))
        return command[0]

    def move(self, enactor, *args):
        dx, dy = args
        if True in [eval(self.rules[rule]) for rule in self.rulebook['move']]:
            pass
        else:
            enactor.set_position(dx, dy)

        target_stage_name = None
        for thing in enactor.stage.things:
            if thing.name == 'portal' and (enactor.x, enactor.y) == (thing.x, thing.y):
                target_stage_name = thing.properties.split(':')[1]

        target_stage = None
        for stage in game.stages:
            if stage.name == target_stage_name:
                target_stage = stage

        if target_stage:
            for stage in game.stages:
                if stage == target_stage:
                    stage.things.append(enactor)
                elif stage == enactor.stage:
                    stage.things.remove(enactor)
            enactor.stage = target_stage

    def ctrl(self, enactor, *args):
        dx, dy = args
        self.close_door(enactor, dx, dy)

    def direct(self, enactor, *args):
        self.get(enactor)

    def indirect(self, enactor, *args):
        self.drop(enactor)

    def get(self, enactor):
        target = False
        for thing in enactor.stage.things:
            if not enactor == thing and (enactor.x, enactor.y) == (thing.x, thing.y):
                target = thing

        if target:
            if True in [eval(self.rules[rule]) for rule in self.rulebook['get']]:
                pass
            else:
                enactor.things.append(target)
                enactor.stage.things.remove(target)

    def drop(self, enactor):
        to_remove = False
        if True in [eval(self.rules[rule]) for rule in self.rulebook['drop']]:
            pass
        else:
            for thing in player.thing.things:
                to_remove = thing
                thing.x, thing.y = player.thing.x, player.thing.y

        if to_remove:
            enactor.things.remove(to_remove)
            enactor.stage.things.insert(0, to_remove)

    def close_door(self, enactor, dx, dy):
        if True in [eval(self.rules[rule]) for rule in self.rulebook['close_door']]:
            pass
        else:
            for thing in player.thing.stage.things:
                if (enactor.x+dx, enactor.y+dy) == (thing.x, thing.y) and thing.name == 'open door':
                    thing.set_name('closed door')

    def open_door_implicitly(self, x, y):
        if player.thing.stage.handlers['Stage'].cell_has_closed_door(x, y):
            for thing in player.thing.stage.things:
                if (thing.x, thing.y) == (x, y) and thing.name == 'closed door':
                    thing.set_name('open door')

    def print_history(self, enactor, *args):
        print self.history
        print player.thing.things

#############################################
# get it started
#############################################

if __name__ == '__main__':
#     try:
#         import psyco
#         psyco.full()
#     except ImportError:
#         pass

    game = Game()
    player = Awesome()

    while not game.exit:
        game.update()
        if not game.exit:
            player.ui.draw()

Here are the yaml files for things. Note that ‘Cyanothus’ currently is hardcoded as the starting stage. Much of the stage functionality is roughly coded at this point really.

---
- 'Cyanothus'
- 0
- 0
- normal
- ''
- 'Stage'

---
- 'window'
- 35
- 12
- normal
- ''
- 

---
- 'closed door'
- 30
- 15
- normal
- ''
- 

---
- 'monster'
- 20
- 9
- normal
- ''
- 'AI'

---
- 'lamp'
- 29
- 6
- normal
- ''
- 'Fov'

---
- 'lamp'
- 41
- 6
- normal
- ''
- 'Fov'

---
- 'lamp'
- 29
- 11
- normal
- ''
- 'Fov'

---
- 'lamp'
- 41
- 11
- normal
- ''
- 'Fov'

---
- 'knife'
- 35
- 15
- normal
- 'gettable'
- 

---
- 'portal'
- 38
- 15
- normal
- 'to:Sexangulare'
-
---
- 'Sexangulare'
- 0
- 0
- normal
- ''
- 'Stage'

---
- 'portal'
- 38
- 15
- normal
- 'to:Cyanothus'
-

The maps are 80×50, and can be any combination of ‘#’ and ‘ ‘ in plain text. The location of things will have to work with the spaces on the map of course.


Angband 3.1.1 released

Source: Angband at oook.cz - Banding News

- 26.7.2009 13:23. New release of the Vanilla Angband is available, featuring many bugfixes, overhauled ID system and a new autosquelcher. See release thread for details. Downloads are available from the homepage as usual: Windows binary, source for Linux people, and OS X installer for Apple crowd. -- Delivered by Feed43 service

Kamyran’s Eye 2 – news 2009-07-25 16:40:00

Source: Kamyran's Eye 2 - news

One more bugfix release candidate has been uploaded.

Random entry

Source: Rogue Hut

Well the plan didn't work for Teemu, but I managed to do LOS for Kaduria.

The vacation is almost over, it's back to work next monday. Sucks.

Played Egoboo that was supposed to be a roguelike. It's an action game with some RPG features.
Categories: Planet Roguelike, Rogue Hut Tags: