new fov (a roguelike in Python #10)

As I began in the last few days to add more things to the map with a field-of-view (starting with some lamps) it seemed like it was getting harder to work in the field-of-view structure I had. Most of this had to do with translation of world to viewport coordinates, and I realized I was calculating things in terms of viewport coordinates in the field-of-view code. Conceptually this seemed a little strange — I decided to keep all game calculations in world coordinates, and only clip to the view port when drawing to the screen.

After a day of thinking and a night of rewriting I think I have it!

newfov

Some more notes after the cut.

Read more »

commands (a roguelike in Python #9)

I’ve been taking a stab at how to structure commands and effects of commands…Up to now I’ve done something simple where the command processing is all done in a function contained in the instance of Command.

So the other day I started trying something like this:


class Command(object):
    def __init__(self):
        self.rulebook = {
                        'move' : ('cell has monster', 'cell is unwalkable')}

        self.rules = {
                        'cell has monster' : 'game.world.cell_has_monster(thing.x+dx, thing.y+dy)',
                        'cell is unwalkable' : 'game.world.cell_is_unwalkable(thing.x+dx, thing.y+dy)'}

    def north(self, thing):
            self.move(thing, 0, -1)          

    def south(self, thing):
            self.move(thing, 0, 1)          

    def east(self, thing):
            self.move(thing, 1, 0)          

    def west(self, thing):
            self.move(thing, -1, 0)  

    def move(self, thing, dx, dy):
        if True in [eval(self.rules[rule]) for rule in self.rulebook['move']]:
            pass
        else:
            thing.x = thing.x + dx
            thing.y = thing.y + dy

The structure of commands/rules/rulebook is a little hazy at the moment, but the idea is that you write a command in the rulebook with a structure of keys. Each key in the rules points to a function, and some functions return True if it’s a simple check — if any are true, the command fails. However some functions could affect the game world, schedule events, create new objects, and so on as well.

This is basically the same as making these checks in the function of the command — calling the functions there. It’s a little easier for me to read the rulebook though to see what rules each command uses, and the list comprehension in the function of the command takes care of adding new rules.

I don’t really want to get too crazy with processing commands (such as calling before and after routines, or checking a bunch of generic ‘can I/am I’ functions for each command), though it remains to be seen if the idea above will be easy enough to work with as I start adding commands and effects.

better coloring (a roguelike in Python #8)

I’ve started to experiment more with cell coloring. Up to now I’ve defined cell colors explicitly — if a cell is of the ground type and visible for example, it was a ‘visible ground’ color, and similarly for a ‘dark wall’ and so on. However I felt this wasn’t a very good way to do colors, especially when you start mixing light and dark and other affects, like mist, smoke, fire, and so on. I don’t want to get too crazy here but I do like the look of colors and it’s one of libtcod’s strengths.

So after a few experiments I have a first pass at what I want to do. It looks like this:

scalelight

If you look at the samples.py file included with the libtcod Python wrapper you’ll see a similar thing going on in the field-of-view sample with the torchlight (though that is more sophisticated, using noise — I hope to do that soon).

My colors dictionary now looks like this:


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

And what I do now when figuring out the color of a cell is find the distance of that cell to the field-of-view origin (for example, the player) and use that distance as a scalar for the affect color. I should note that affects are colors like ‘dark’, ‘lit’, and so on. Also you don’t really need to find the distance, just the distance squared to get the appropriate scalar — this will save a bit on computation, or so I’ve heard.

In other words, the ‘visible’ affect is most intense at its point of origin, and least at the maximum distance of the field-of-view radius. I think it does look better than using hard edges on affect boundaries.

This scalar is linear at the moment, so it creates a somewhat odd but not displeasing affect to my eyes. However I think I do want to randomize it a little with noise as done with the torchlight for samples.py.

saving part one-and-a-half (a roguelike in Python…#7)

I knew there was a reason I liked Python. In the last post I was going back and forth some on the readability of pickled files, so later tonight I looked a little closer at the PyYAML module. All I needed to do was change the pickle function calls to yaml function calls (as in, change pickle.dump to yaml.dump — that’s it!) and the files now look like this:


!!python/object:__main__.Actor
brain: &id001 !!python/object:__main__.Brain
  fov_map: 9773936
  handlers:
  - !!python/object:__main__.Fov
    brain: *id001
    fov_map: 9773936
    fov_radius: 8
    light_map: 9773904
  light_map: 9773904
  speed: normal
name: player
speed: normal
x: 32
y: 16

Of course I don’t know very much about the differences of pickle and yaml at this point (I knew I liked yaml from earlier exposure to it in some mud codebases) but this file format is at least looking much more to my taste!

saving part one (a roguelike in Python #7)

I’ve been having a go with saving and loading in the Python demo. It may seem a little silly to do this before I have a game or for that matter, any content at all, but I’m following a bit of advice I read on rgrd and I’m glad I’m doing it…especially with loading the save state back in, it makes you think of how you structure the data to be saved in the program in the first place.

Luckily this first try is rather simple given Python’s pickle module. To save on exit I put this in the main loop:


            if player.input.key.vk == libtcod.KEY_ESCAPE:
                file = open('centaur.sav', 'w')
                file2 = open('player.sav', 'w')
                pickle.dump(game.world.level_state, file)
                pickle.dump(player.thing, file2)
                file.close()
                file2.close()
                break

This saves the level data and the player object to two separate files, then closes the game — though game.world.level_state doesn’t actually change at all currently, the x,y position of the player object does change, and saving it allows you to start the demo, move the player, then restart the demo with the player in the new spot.

For a quick test I just did this in place of the creation of the player thing object;


        if os.path.isfile('player.sav'):
            print 'player file found'
            file = open('player.sav', 'r')
            self.thing = pickle.load(file)
            game.world.things.append(self.thing)
            file.close()
        else:
            self.thing = Actor('player', 26, 16, Fov)

Note that I needed to add the player object back to the game.world.things list — otherwise, the player object brain’s update method will never be called (nor its field-of-view handler), as you aren’t adding it to the list by instancing the Actor class! Not doing this at first was a strange sight, as I moved and changed the player’s position without the screen updating.

As I mentioned, this test was a good thing to do early, as now I’m looking at how to arrange these saving and loading methods in the code. It seems like it would be a good idea to consolidate the saving and especially the loading as much as possible. Furthermore I’m not completely sold on the pickle module, as it creates files that look like this (in ASCII mode):


ccopy_reg
_reconstructor
p1
(c__main__
Actor
p2
c__builtin__
object
p3
NtRp4
(dp5
S'y'
I6
sS'x'
I18
sS'speed'

I’m envisioning a case where I have a save file and I want to examine it without too much fuss. A simple text format would be much more readable. On the other hand, the pickle module is dead simple, and it is readable (and unpickle-able, of course).

updates in a minor key (a roguelike in Python #6)

The Python demo has seen a lot of small changes as I attempt to drag it kicking and screaming into something a little more usable for a real game.

centaur01

It’s at about 500 LOC at the moment and not yet incomprehensible to me — a good thing I think since I’m not working on it every day — and posted in full below, but I’ll try to highlight the bigger changes first.

Read more »

rogue speed (roguelike in Python, #5)

Tonight I made a rough draft of the phase order speed system I talked about earlier and described by Jeff Lait here.

roguedemo

You can see that the phase count (the turns in ‘update time’) is higher than the turn count (’game time’) as phase count increments with the fast and quick phases.

Right now the implementation is pretty simple…I’m trying to keep everything readable, somewhat verbose and obvious. So here is what I put in the game class:


        self.phases = ['fast', 'normal', 'slow', 'quick', 'normal']
        self.phase_dict = {
                            'fast' : ('fast', 'normal', 'slow'),
                            'normal' : ('normal', 'slow'),
                            'slow' : ('normal'),
                            'quick' : ('normal', 'slow', 'quick'),
                            'fastquick' : ('fast', 'normal', 'slow', 'quick'),
                            'fastslow' : ('fast', 'normal'),
                            'quickslow' : ('quick', 'normal'),
                            'fastquickslow' : ('fast', 'normal', 'quick')}

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

Then after you give a brain a speed like ‘normal’ or ’slow’ you can test if a thing takes a turn on a particular phase with this conditional:


if game.phase in game.phase_dict[brain.speed]

Probably a little crude, and it may get a little convoluted to change speeds later (from fastquickslow, say, to fastslow), but this was a simple way to get the ball rolling.

Object-oriented, second attempt (a roguelike in Python #4)

It’s been a while since the first attempt, but better late than never, here is I think an at least better organized object-oriented roguelike demo. There are a few main differences with the first attempt; my goal here was to do a better job of separating the game logic from the game state, and the view on the screen from the working of the game itself. I’ll just run quickly through the whole thing:

Read more »

more detours into scrolling (roguelike in Python #3.5.a.i)

Maybe it seems like I’m obsessed with scrolling, but bear with me. After Ludum Dare and PyWeek I’m getting back into the roguelike demo. I knew that the scrolling code was kind of a mess. I won’t bother reposting it here (you can see it here), but basically it was a few if statements for each axis inside a for loop, all to determine where to place the scrolling view on the map and where to draw the focus of the view (typically the player).

Looking at it tonight I realized a few things. First, I definitely don’t need to check where to draw the view focus inside the for loop. And second, I don’t need to determine where to place the scrolling view on the map inside the for loop either!

All I need to know is where the view should be placed. Then the for loop can just start from there. In addition, the placement of the view focus is made much simpler by using basic addition rather than a few if statements.

So here’s the new update method for the scrolling code:


    def update(self):
        self.cells = []

        self.left_view_frame = min(max(0, self.thing.x - self.width//2), world.level_width - self.width)
        self.top_view_frame = min(max(0, self.thing.y - self.height//2), world.level_height - self.height)

        x_left_offset = min(self.thing.x, self.width//2)
        x_right_offset = max(0, (self.width//2 - (world.level_width - self.thing.x) + (self.width % 2)))
        self.view_x =  x_left_offset + x_right_offset

        y_top_offset = min(self.thing.y, self.height//2)
        y_bottom_offset = max(0, (self.height//2 - (world.level_height - self.thing.y) + (self.height % 2)))
        self.view_y =  y_top_offset + y_bottom_offset 

        for i in range(self.height):
            self.y = self.top_view_frame + i
            for j in range(self.width):
                self.x = self.left_view_frame + j
                self.cells.append((self.x, self.y, j, i))

One note about the (self.width % 2) (which applies to y as well) — thanks to Grishnak at the Doryen Library forums I’ve added this fix for the case where the viewport is an even number of cells wide or tall. I was adding one before, as I normally use an odd-sized viewport.

pong #2

open_title_screen

PyWeek just wrapped up and with it my second game, open. There are some amazing looking entries there. Between this and Ludum Dare I’m probably set until 2010.

People say that when you start making games you should make games with the scope of Pong or perhaps Tetris. I feel that’s where I’m at right now with these last two (well, probably not to Tetris yet). I’m happy though not to have made an actual Pong or Tetris (OK, I have made Pong, but it was basically a type-in). I feel like I’ve learned a lot making abandon and open, that I wouldn’t really have learned making a clone of some existing game. So, make your Pong and your Tetris, just understand what that means for you.

Next Page »