This week I spent some time getting a limited chat up and running, along with basic procgen for the map and some extra hooks for HP. All the screenshots will have the new procgen in there, but I’m going to talk about chat first.

Before starting though, I am pleased to report that this does work in a public environment. I put the repo on a Google Cloud virtual machine (microinstance) and played it with a friend for a bit. No lag from either end at this point, though 2 people isn’t really a stress test. It was more of a “does it actually work outside of my own machine” type of test.

And that, at least, passed just fine.

yay

Chat

I wanted players to have a limited chat so that they can talk to each other. Otherwise we lose a bit of the MMO-ness. In taking a page from RotMG, I thought little speech bubbles would be a good idea.

rotmg chat

We’ll get there eventually, but each person only says one thing at a time - there is no buffer. What I did was to give each MoveableEntity a chat object - currently a list but that may get pared down a single object. The list is forced into a mutable tuple - element 0 is the chat message and element 1 is the timer.

The timer will decrement for each game tick that a chat message exists on an entity, allowing me to map opacity to time remaining. In its current state things decrement quickly, but are visible and functional at least.

The nice bit here is that all MoveableEntity objects get a chat, so enemies can now talk to you as well! This will eventually extend to NPCs once they get added.

The screenshot is a bit hard to see below, but the enemies will randomly select a bit of angry flavor text when they’re following you. The rat is saying ‘!!’ at the moment.

rat chat

Colors and interface are by no means final and currently quite difficult to read, but I figured prettifying it would take a back seat to actual implementation. Plus then I’d need to add things like text wrapping and all those fun calculations and my eyes glazed over a bit.

Work for future me.


Procgen

Ok, now for procedural generation.

Deep breath

I didn’t use tcod. I apologize to all. I just … didn’t feel like rewriting all of my code to handle the numpy arrays. In the future that would be a better idea for performance, however for now we’ll just stick with a 2D list.

Otherwise, this proof of concept will never be done.

Anywho, I used the opensimplex noise library (PyPi reference) and simply mapped ranges to tile values. As you can see below the level is fairly water-heavy. I’ll be tweaking this significantly as I go, but I honestly like how it looks at the moment.

Pretty simple mapping that is fun to play with for a ‘standard’ Simplex noise look:

n = opensimplex.noise2(c*zoom, r*zoom)
newtile = "empty"
if n < -0.8 or n > 0.8:     # water2 is not walkable
    newtile = "water2"
elif n < -0.6 or n > 0.6:
    newtile = "water1"
elif n < -0.4 or n > 0.4:
    newtile = "floor2"
elif n < -0.2 or n > 0.2:
    newtile = "floor1"
_map[z][r].append(newtile)
Floor 1
procgen 1

Plus, as you descend in Z-levels the zoom changes as well. This comes care of a handy utility function I always transpose from p5js:

# map function similar to p5.js
def p5map(n, start1, stop1, start2, stop2): 
    return ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2

# map generation loop
for z in range(self.NUM_LEVELS):
    zoom = p5map(z, 0, self.NUM_LEVELS, 0.1, 0.001)
    ...
Floor 3
procgen 2

Gives a nice feeling of depth and the environments varying.


Other

I also updated the drops/items a bit so that players could eat apples, rather than just collect them. To make this happen, a new ‘use’ key got added (right now that only eats apples) and heals the player (and plays the associated pickup sound since I didn’t feel like sound hunting at the moment). To heal the player a new updateHP function got added to MoveableEntity that will be the basis of HP management and death. If HP < 0, then the function will return false, otherwise it will cap at the entity’s maxHP attribute. No HP overloading planned as of this point.

However, testing this functionality was not really possible as nobody can hurt you. So, all players got started with 2 HP for a bit just to make sure that all the socket communication was happy.

Test test test!

…then test again.

One other bit I added was some sprite management. Each tile now has a named key, an associated glyph, and a color value (including alpha). This is all done in hex code on the client-side (I’m making the assumption all game logic happens server-side and all drawing/rendering/UI/sounds happens client-side):

// spritesheet
let SPRITESHEET = {
    // env
    wall1: { sprite: '#', color: '#ccc' },
    wall2: { sprite: '#', color: '#999' },
    floor1: { sprite: '.', color: '#333' },
    floor2: { sprite: '.', color: '#666' },
    water1: { sprite: '~', color: '#008080' },
    water2: { sprite: '~', color: '#0011ee' },
    empty: { sprite: ' ', color: '#000' },
    stairsDown: { sprite: '>', color: '#00ffff' },
    stairsUp: { sprite: '<', color: '#00ffff' },
    ...
}

To me, going this route is nicely extensible as I can also tack on sprite images as well if I’d like, or offsets into a spritesheet. Previously it was all if checks in code for the exact sprite, so not very programmer-friendly.


Next week I plan to work on enemy AI a bit, since right now everything either wanders or follows you. Also, I’d like to start dipping into accessibility a bit to ensure that the game itself is playable for all. I don’t have a whole lot of experience in managing that with a canvas, so some additional research is going to be needed.

Here’s a GIF of current playing through a few levels and testing out chatting. Even though I didn’t need any, I just couldn’t stop myself from picking up some apples.

playthrough

Last post

Next post