This week I wanted to start by turning my “simple” entities into proper objects. Previously anything that moved was simply a row/column position dictionary. Nice for prototyping, bad for anything else.

Somewhat following a mishmash of multiple tutorials I created a base Entity class and a child MoveableEntity class, where Entity is intended to not move (e.g., items). Every object also now gets a unique identifier so that I can track them, care of the uuid Python class.

I may end up needing to refactor as I’m not truly following the tutorial and just using it as an excuse to work on yet another roguelike, however here is where we’re at:

# Base entity class
class Entity:
    def __init__(self, _type, pos, entity_id=None, count=None):
        assert _type in ENTITY_NAMES, "Error: ${0} not found in lookup table.".format(_type)

        self.entity_id = entity_id
        self._type = _type
        self.pos = pos
        self.count = count

    def getTransmissable(self):
        return {
            'type': self._type,
            'pos': self.pos,
            'count': self.count,
        }

# Entity that can move around the screen
class MoveableEntity(Entity):
    def __init__(self, _type, pos, entity_id=None):
        super().__init__(_type, pos, entity_id)

        # particulars
        self._type = _type
        self.pos = pos
        self.entity_id = entity_id # only used for logged in players

        # stats
        self.hp = LOOKUP_STATS['maxHP'][_type]
        self.maxHP = LOOKUP_STATS['maxHP'][_type]
        self.active = True
        self.inventory = {}

    def getTransmissable(self):
        return {
            'type': self._type,
            'pos': self.pos,
            'hp': self.hp,
            'maxHP': self.maxHP,
            'active': self.active,
            'inventory': self.inventory,
        }

Couple of points here that were important to me. I have a LOOKUP_STATS dictionary in global space that serves as my overall balancing table for entity stats - who has what HP, etc. I also have an ENTITY_NAMES list that is the global array of the names for all things that can be in the game (mainly as a check to make sure I didn’t typo anywhere when instantiating entities). Most likely I’ll move those to a dictionary that serves as a name/description table, but that’s for future me (insert technical debt here).

The other bit is the getTransmissable function. This serves to break the object down into what gets sent along the socket so that we’re not packaging up the entire thing - only what matters to the frontend. There are probably better ways of doing this, but I’m trying to keep it as light as possible.

So now, I can instantiate everything as an Entity or MoveableEntity, neat.


Next up is to add in items to be scattered around for the player to pick up. Here I used my new objects and created a new list focused around items - in this case apples. Right now they serve no purpose other than to be picked up and added to the player’s inventory, however it works quite nicely.

picking up apples

With some additional socket work (i.e., also sending the items array as well as the player inventories) this behavior also works nicely with multiple players. Not too hard as the logic is pretty much the same as enemy updates and was mostly copy and paste.


This leads me to one future point that I should address. Right now all of my things in the game are separate in that I have a dictionary of players (keyed on the session ID provided by Flask), a list of enemies, and a list of items. It works, however it really isn’t to my liking for efficiency. At some point in the near future I’ll merge this into a single Entities object (or perhaps delineate Entities and MoveableEntities) and try to capture the logic into a bastardized Entity-Component pattern.


Last update for the week - laying the groundwork for multiple levels/floors/instances/etc. A new attribute was added to the position of all Entity objects: level.

I have a helper function that places the Entity on a clear spot within the map (no walls, etc.):

    def getRandomPos(self, level=1):
        r = random.randint(0,self.NUM_ROWS-1)
        c = random.randint(0,self.NUM_COLS-1)
        while not self.isWalkable(c, r):
            r = random.randint(0,self.NUM_ROWS-1)
            c = random.randint(0,self.NUM_COLS-1)
        return {'r': r, 'c': c, 'level': level}

Now, it takes in the desired level of the object and includes it as something very easy to lookup. The change cascades all over the current code, however any time there is a lookup for row/column, now the level is checked as well. For instance, enemies only follow players on their level, socket data only sends current floor information relevant to the player, etc.

You may have noticed there aren’t any enemies in the last gif - that’s because I spawned them all on level 2 to check that my inventory/pickup system was working without needing to despawn them all:

def addEnemy(self):
    return MoveableEntity("snek", self.getRandomPos(level=2), str(uuid.uuid4()))

That’s it for now - next week I’m hoping to add a camera system and/or a proper procedural content generation algorithm for the dungeon. Or focus on a user interface so I don’t need to keep calling console.log any time I want to see data 🙄.


Last post

Next post