This week introduces player area of effect spells (singular currently), a Drunkard’s Walk algorithm for map generation, and chat backgrounds for players (enemies lose theirs). Mostly it was in touching up some things on the backend to better support the effects.

Also, enemy pathfinding gets a touch of rework so that enemies “forget” about players.


Chat Backgrounds

I was really not up for doing the calculations necessary to draw a chat balloon in the canvas, so I turned to our old friend StackOverflow to see if there were pre-existing solutions to the problem. Fortunately, chat bubbles are pretty common things to have, so there were numerous questions out there for how to draw them.

I liked this one the most, though I removed the little directional bit (for now): https://stackoverflow.com/questions/21597644/speech-buble-html5-canvas-js

player chat

Enemies also lose their bubbles because the screen got really messy with everybody chattering. I still think I prefer this to a message log though - we’ll see if “important” messages still get logged. It makes sense for a traditional roguelike, though here I don’t know that it does.


Procgen - Drunkard’s Walk

First off, I tweaked the order of generation. The overworld gets the Simplex noise forest and sub-levels are now created via a drunkard’s walk. The tentative plan is for sub-levels to have their own algorithms creating them (caves, dungeons, etc.). I think if and when I get to an actual narrative those choices will become clearer.

However, this one is easy enough. Fill a map with walls and then carve out walkable spaces based on the stumblings of a drunkard. My implementation currently does not take connectivity into account, so there are chances that I’ll have disconnected areas.

Targets of optimization for a future flood fill check (when a cellular automata gets added). Right now I give it enough iterations/time that it hasn’t been an issue for the smaller map size.

Here’s the algorithm - again, fairly simple. ENEMY_DIRS is a global list of possible directions that enemies can walk in (essentially the cardinal/ordinal directions) that I just reused here.

def drunkardsMap(self, z):
    _map = [['wall2'] * self.NUM_COLS for _ in range(self.NUM_ROWS)]

    center_c = self.NUM_COLS//2
    center_r = self.NUM_ROWS//2

    for _ in range(DRUNK_ITERATIONS):
        curr_c = center_c
        curr_r = center_r
        DRUNK_LIFETIME = self.NUM_COLS * self.NUM_ROWS

        for i in range(DRUNK_LIFETIME):
            _map[curr_r][curr_c] = random.choice(['empty', 'empty', 'floor1', 'floor1', 'floor2'])

            new_dir = random.choice(ENEMY_DIRS)
            curr_r += new_dir['r']
            curr_c += new_dir['c']

            # out of bounds
            if curr_c == 0 or curr_r == 1 or curr_c == self.NUM_COLS-1 or curr_r == self.NUM_ROWS-1:
                break

    return _map

Here we have the player going down to the first sub-level. Looks like a fairly standard randomly-carved out space!

drunkard

(Note to future self: fix the odd offset between ASCII and Unicode characters):

offset

AoE - Fire

This took a little longer than I thought as I had figured I could re-use my Item entity setup for spawning fireballs surrounding the player. It would have been more special handling than I wanted in that class, so effects are now their own thing.

What it boils down to is that I spawn a set of effect objects, give them a lifetime, define what they do, and then track them like all other entities.

Note: what should probably happen is that every entity gets their own update function and self-handles - I haven’t gotten there yet with refactoring.

However, for now it just goes into the lookup table:

# key: {attr:impact, timeout: upper}
'effects': {
    'fire': {'hp': -3, 'timer': 10},
    'heal': {'hp': 1, 'timer': 5},
}

The timer field is the upper limit of how long it lasts. Essentially a target for future randomization (perhaps the fire lasts for random.randint(5, LOOKUP_STATS['effects']['fire']['timer'])).

Anyway, here’s a shot of some rats and goblins getting toasted:

fire!

Right now it doesn’t hurt the player to walk over their own flames, though I might change that up as it seems a bit overpowered to leave firebombs burning while the player escapes unharmed.

I’d really like AoE effects to interact with the environment (fire burns grasses, trees, etc.), but we’ll see how that pans out. To do that I’d need a separate hook for the environment to respawn over time, then we’re talking going down the path of simulating a world’s ecological systems, and that might be a bit too much to bite off right now.

Other spell targets are a group heal, electricity, etc.


Enemy Following

Enemies now follow a particular player rather than simply wandering and locking on to whoever is closest. Enemies will wander until a player is close enough (currently the camera range) and will then try to follow that person. If the player goes out of range the enemy will forget about them and go back to wandering/scanning.

Essentially, players can now escape the clutches of the hordes of the underdark. No GIF here as it would be fairly boring, but enemies will basically stop following you if you just walk away from them.

(This is somewhat extensible to different follow/forget types of enemies as well - larger/smaller radii, etc.)


Last little bit - I started breaking up the files into specific uses - e.g., utility functions go in utils.js, etc. I’ve been keeping everything in single files just to avoid navigating through 30 different files (something that is annoying during development to me personally - I know that long term it is a terrible idea and freely admit that). However, for posterity things are getting broken apart and placed into their proper locations. Not done yet, but the process has begun.


Next time I’d really like to get some sort of feedback from attacks, as the bump to attack feels very unsatisfying at the moment. I’ve been playing with the sound off as well, so no additions there.

Also, more procgen algorithms! Eyeballing cellular automata and BSP generation.


Last post

Next post