More work on Character Sheets, and a feature idea from Twitter follower WR.

In response to my recent article about new monsters, Twitter follower WR suggested:

How about a monster called Orange Man, very incompetent, swings and misses a lot but still has a bad effect to everything around him.

I’m not sure that I understand the source of the idea, but it does sound like an interesting kind of monster, a strange mix of maleficence and clumsiness, ill-will and incompetence, thrashing around pointlessly but somehow garnering favorable attention from other dungeon residents. It might make for an interesting implementation problem. I’ll definitely add it to the implementation list.

For our existing monsters, there’s a kind of implicit limit on the strength and health implied by the current MonsterSheet implementation:

serpent

There’s only room for about 5 icons in each attribute row. That doesn’t allow for much variation among monsters, and if we followed a similar pattern for the player, not much room for growth. If this were a real game, we’d have to do something about this, probably replace this display with a numeric value, perhaps decorated with the fancy icon. Since this is a programming demonstration about what happens when we set out to create a game, I think we’ll let it be, and work within the 0-5 value range. We could always go to two points per icon or something. For now, we’ll let it be.

Varying Monster Values

Now that we have the ability to show strength and health for monsters, it would probably be a good idea to set them up with different values. All we really need to do is to add corresponding values in their table entry. And doing so will let me clean up a bit of the monster code, I think. So stand back while I revise the monster table. I’ll spare you the tedium and show you the next one I go with.

    m = {name="Pink Slime", health=1, strength=1,
    dead=asset.slime_squashed, hit=asset.slime_hit,
    moving={asset.slime, asset.slime_walk, asset.slime_squashed}}
    table.insert(MT,m)
    m = {name="Death Fly", health=2, strength=1,
    dead=asset.fly_dead, hit=asset.fly_hit,
    moving={asset.fly, asset.fly_fly}}
    table.insert(MT,m)
    m = {name="Toothhead", health=4, strength=2,
    dead=asset.barnacle_dead, hit=asset.barnacle_hit,
    moving={asset.barnacle, asset.barnacle_bite}}
    table.insert(MT,m)
    m = {name="Murder Hornet", health=1, strength=3,
    dead=asset.bee_dead, hit=asset.bee_hit,
    moving={asset.bee, asset.bee_fly}}
    table.insert(MT,m)
    m = {name="Ghost", health=5, strength=1,
    dead=asset.ghost_dead, hit=asset.ghost_hit,
    moving={asset.ghost, asset.ghost_normal}}
    table.insert(MT,m)
    m = {name="Serpent", health=1, strength=3,
    dead=asset.snake_dead, hit=asset.snake_hit,
    moving={asset.snake, asset.snake_walk}}
    table.insert(MT,m)
    m = {name="Vampire Bat", health=5, strength=1,
    dead=asset.bat_dead, hit=asset.bat_hit,
    moving={asset.bat, asset.bat_fly}}
    table.insert(MT,m)
    m = {name="Yellow Widow", health=1, strength=4,
    dead=asset.spider_dead, hit=asset.spider_hit,
    moving={asset.spider, asset.spider_walk1,asset.spider_walk2}}
    table.insert(MT,m)
    m = {name="Poison Frog", health=2, strength=4,
    dead=asset.frog_dead, hit=asset.frog_hit,
    moving={asset.frog, asset.frog_leap}}
    table.insert(MT,m)
    m = {name="Ankle Biter", health=3, strength=1,
    dead=asset.spinnerHalf_dead, hit=asset.spinnerHalf_hit,
    moving={asset.spinnerHalf, asset.spinnerHalf_spin}}
    table.insert(MT,m)

I’ve given the monsters various levels of health and strength. For example, the Yellow Widow is easy to kill, but has strength 4, so will do serious damage. In a few minutes here, I plan to roll random damage rather than just use the value directly.

The Ankle Biter, which is a circular saw blade embedded in the floor, is hard to kill but does low damage. And so on.

Here’s a rogue’s gallery of all the monsters as of now:

rogues

Let’s commit: new monsters, all have health and strength.

Now to deal damage. First let’s see how we init the princess:

function Player:init(tile, runner)
    self.alive = true
    self.tile = tile
    self.tile:illuminate()
    self.tile:addContents(self)
    self.runner = runner
    self.keys = 0
    self.hitPoints = 5
end

I called that healthPoints in Monster, let’s rename it. And we’ll leave her at five for now.

Damage is done from a Tile. I’m not sure why I made that decision, probably because I was thinking of having a little automatic motion during a longer battle. Anyway here’s where the player receives damage:

function Player:damageFrom(aTile,amount)
    if not self:isAlive() then return end
    sound(asset.downloaded.A_Hero_s_Quest.Hurt_1, 1, 1.5)
    self.healthPoints = self.healthPoints - amount
    if self.healthPoints <= 0 then
        sound(asset.downloaded.A_Hero_s_Quest.Hurt_3,1,1.5)
        self:die()
    end
end

That’s sent here:

function Monster:startActionWithPlayer(aPlayer)
    aPlayer:damageFrom(self.tile,1)
end

We want to roll random damage now. Zero should be possible, up to the monster’s strength:

function Monster:startActionWithPlayer(aPlayer)
    aPlayer:damageFrom(self.tile,math.random(0,self:strength()))
end

I suspect we’ll want to display something about the damage done, but not yet. The player attacks the monster the same way:

function Player:startActionWithMonster(aMonster)
    aMonster:damageFrom(self.tile,1)
end

That becomes:

function Player:startActionWithMonster(aMonster)
    aMonster:damageFrom(self.tile,math.random(0,self:strength()))
end

I was tempted to give the player a guaranteed damage of at least one, but we’ll play fair for now. I’ve given her a starting strength of 2, so she is not as strong as some of the monsters. Let’s hope her heart is pure.

We had better start putting potions or something in the dungeon to deal with healing, or she could be in big trouble.

I’m starting the level with only three monsters, so exploration should be a bit more safe. But there’s no limit on what kind of monster might show up.

Oh, a note for my yellow tablet: when all monsters are killed, start spawning new ones.

Ah. During testing, I found a thing I’ve seen before. Sometimes the picture in the monster sheet shows up black. I’m sure that’s happening because the monster is still in shadow and is being tinted black. Let’s see if we can see what’s going on …

Meh. I’m not sure. Let’s fix this with the hammer:

function MonsterSheet:drawPhoto(aSprite)
    self:newLine(4)
    tint(255)
    sprite(aSprite, 80, 0)
end

That’ll do it.

Tuning

I’ve done a bunch of tuning. I changed the tinting of dead creatures so that they are a bit ghostly, and greenish, but still visible. (They all have nice x-ed eyes, like all dead things do.) I changed them so the monster sheet only shows when the monster is within 5 cells of the player. I tweaked a few other little graphical bits here and there.

dead fly

In my experience, this sort of tuning is very common in a game, as we adjust it to be more attractive or more playable. If the code is fairly well structured, such changes are easy. To the extent that it is not well-structured, we can find ourselves digging around to find where something is set, or where it is overridden, or where the override is overridden, and tuning changes become slow, difficult, and tedious.

Mostly, here, it’s pretty simple. However, there is a bit of messiness going on in the display logic because part of it thinks it’s still possible to scale the whole map to full screen size, but the ability to turn that on has been removed. That code should be cleaned up, which should improve things a bit. However, we do still draw the map twice, once zoomed in with the circle of darkness, and once, zoomed way out, so there will still be a bit of oddness going on. We’ll work on that in due time.

Summing Up

The monster sheets, and monster enhancements, haven’t been very interesting. There will be some learning in dealing with treasures, and we’ll want to do something about making the levels have some kind of increasing difficulty. Perhaps the player can gain armor or weapons that will add to their ability to fight and survive.

I’d like to devise some kind of display of damage done, both by monsters and by the player. That might be a good thing for next time. And we need to display the player’s status similarly to that of the monsters.

I welcome ideas about what would be interesting to try to do. I don’t promise to do anything too weird, like convert to 3D, but a reasonable idea that might be difficult to do could make for an interesting series of articles. Tweet me up if you have ideas.

See you next time!


D2.zip