oh no you mean I have to add things? (a roguelike in Python, #12)

Up to now I’ve been motoring along quite happy with my simple arrangement — a player, a monster, some lamps, field-of-view, saving and loading. In theory I’d written the code as a framework that would make it easy to add stuff, but…I hadn’t actually added any stuff yet. I knew this was an important step. Would things totally break down? How easy would it be to add stuff?

So I’ve started to add stuff. Let’s see how easy it is to add something. Trying to keep things simple, I’m starting with windows and doors.

Now I already had windows, but these were a part of the hardcoded map. I’ve decided to make them of the Thing class instead, and so the hardcoded map will strictly represent terrain (at the moment just walls and ground). To make a window a thing this is what I had to do:

  • remove the window from game.world.cell_types and the hardcoded map
  • add a test to Fov so that if a cell in field-of-view is a window, it’s marked as transparent in the fov map.
  • add the window to player.ui.chars
  • remove adding the window to the viewport structure in the view.update, as now it’ s just drawn with the other things.
  • create the window thing in Game.

Adding doors followed a similar path as above (minus removing code of course as doors are totally new), but I also had to add the functionality of opening doors. So, I went to the Command class. Here is what the rulebook and rules look like now:

class Command(object):
    def __init__(self):   
        self.rulebook = {
                        'move' : ('cell has monster', 'cell is unwalkable', 'cell has closed door', 'open door implicitly')}
                        
        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)',
                        'cell has closed door' : 'game.world.cell_has_closed_door(thing.x+dx, thing.y+dy)',
                        'open door implicitly' : 'game.world.open_door_implicitly(thing.x+dx, thing.y+dy)'}

So I added ‘cell has closed door’ and ‘open door implicitly’ (bump to open — now that I think of it I might be able to make a general ‘bump to open’ rule). Here are the corresponding methods in World.

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

Though I set the name directly here, I think I’ll change this later to call a method on Thing to change its name. I’m trying to keep World as a state object only, changing its state from outside this object.

In player.ui.chars I have this:

        self.chars = {
                        'player'    : '@',
                        'monster'   : '?',
                        'ground'    : ' ',
                        'wall'      : '#',
                        'window'    : libtcod.CHAR_DHLINE,
                        'lamp'      : 'Q',
                        'open door' : '/',
                        'closed door'   : '+'}

So when I change the name to ‘open door’, the symbol changes, and the movement test for a closed door will pass as well.

Overall, not too bad. I think I’m going to have to generalize the field-of-view code a bit, as I can forsee adding a lot of special cases as I add objects if I keep doing what I’m doing. So far I’m happy with how the rulebook and rules works too.

update:

To make a close doors command required a few more changes. This requires either a command then command input (like a ‘c’ key then direction) or a combination key like command + command. I’ve decided to go with the latter, and I’m using CTRL + direction. I think I like the idea of extending this in the future into a context sensitive command as well. As libtcod doesn’t currently have an easy way to test for a combined key press (I think it’s slated for 1.5) I just made a boolean in player.input like so:

class Input(object):
    def __init__(self):
        self.update_state = self.intro_update
        file = open('keys.cfg', 'r')
        self.keycfg = yaml.load(file)
        self.ctrl = 0

    def get_key(self, key):
        if key.lctrl:
            self.ctrl = 1
        else:
            self.ctrl = 0
        
        if key.c:
            return chr(key.c)
        else:
            return key.vk

Every time I call get_key() (when a key is pressed) I check if the left control is pressed and then set the self.ctrl value accordingly. Then in Command I can do this:

    def north(self, enactor):
        if player.input.ctrl:
            self.close_door(enactor, 0, -1)
        else:
            self.move(enactor, 0, -1)  

And then add the rules and a command for close_door() like normal.

Advertisements

No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: