Let’s do some more user input. And some planning, not necessarily in any particular order.

If we’re going to handle multiple attackers, as has been requested, then I think we need to take the next step toward a turn-based combat system. The step I have in mind is player input. I think we need some kind of input thingie, a popup window, or maybe just more buttons like the arrow keys.

I’ve also encountered two wonderful people from the Codea group, Dave and Bri, who have been trying the program, and, unfortunately for me, they have different sized screens from mine. And I think at least one of them plays in portrait mode, while I play in landscape. I’m not at all sure that I want to address that level of screen scaling today, but I need to start thinking about it. So I mention it here, so that the idea begins to perk.

I’ve not yet planned out a turn-based combat scheme, but we can be sure that it involves the player and the monsters taking turns. In D&D, I believe, all the monsters go at once, then the player, etc. In D&D you have a part of players, so in that game it’s more like the monsters go at once, then player 1, then 2, and so on. We only have one player, though it might be interesting if we allowed a party. (Please, no one mention that idea to the product owner.)

I think I want to retain the notion of the crawl, or Floater, that describes the battle in a scrolling bit of text above the action. The main issue that I foresaw with that was the concern that if we give the payer a chance to make a decision, there will be nothing to put in the crawl until the player decides what to do, which could be a long time in computer years.

But I had an idea. Imagine our coroutine, that yields a line of text every time it’s called. Suppose it enters a state where it needs player input. If we can rig the input so that we can detect whether it’s present or not, we could cause the coroutine to return “…” or “player dithers” or any message, until the information becomes available. We have to return something. We can’t just hang in a loop, because if we do the whole game will hang in a loop and that would be bad.

I even imagine, as through a glass darkly, that the Floater object could pause the crawl’s increment, if displaying line after line of “…” got irritating. The main thing is that we ask for all the information we need and get it, and then get the heck out of town so the next draw cycle can begin.

So this makes me want to have a way for the player to specify some additional action during combat. Given that, I think we’ll replace the current Encounter coroutine with a different, simpler one, representing just one combat turn cycle. Then we’ll build back up from there.

So, we need screen input. Let’s start with what we know.

The Buttons

We have a simple Button object”:

function Button:init(name, x,y,w,h, img)
    self.name = name
    self.x = x
    self.y = y
    self.w = w
    self.h =  h
    self.img = img
end

function Button:draw()
    pushMatrix()
    pushStyle()
    spriteMode(CENTER)
    translate(self.x,self.y)
    tint(255,255,255,128)
    sprite(self.img, 0,0, self.w, self.h)
    popStyle()
    popMatrix()
end

function Button:touched(aTouch, player)
    if aTouch.state ~= BEGAN then return end
    local x = aTouch.pos.x
    local y = aTouch.pos.y
    local ww = self.w/2
    local hh = self.h/2
    if x > self.x - ww and x < self.x + ww and
       y > self.y - hh and y < self.y + hh then
        player[self.name](player)
    end
end

Note what happens at the end, in touched. We think of each button as a rectangle, and if the touch is inside that rectangle … we look into the player (whom we know), for a method whose name is the same as our name variable, and we call it (passing `player’). That’s a bit cryptic because Lua, but what it means is that if our button’s name is “moveLeft”, we call

player:moveLeft()

That code is just syntactic sugar for what Lua really does, which is precisely what our code does above, namely:

player["moveLeft"](player)

That’s how message dispatch works in Lua classes.

So, bottom line is, a button calls a method on player.

This seems good enough to me. We can create more buttons, and maybe turn them on or off or something, and when they are touched, the player object knows what the player human wants to do. From there, well, the program does whatever it is. Details at 11.

Let’s begin with something really simple, even silly. Let’s give the player two new buttons, “Fight” and “Flee”. At first, we’ll just set some kind of a state flag, but the idea will be that at the end of an Encounter, we’ll either attack again, or run away if we can.

I honestly have no sold idea about how we can do this. This is, in an important sense, the starting point for almost all programming. If we’re lucky, experienced, skilled, familiar with all the code, fortunate, and lucky, an idea will come to us soon. When it doesn’t, we ask around, or check stack overflow.

Let’s just add two more buttons to start.

    self.buttons = {}
    table.insert(self.buttons, Button("left",100,200, 64,64, asset.builtin.UI.Blue_Slider_Left))
    table.insert(self.buttons, Button("up",200,250, 64,64, asset.builtin.UI.Blue_Slider_Up))
    table.insert(self.buttons, Button("right",300,200, 64,64, asset.builtin.UI.Blue_Slider_Right))
    table.insert(self.buttons, Button("down",200,150, 64,64, asset.builtin.UI.Blue_Slider_Down))
    self:createCombatButtons(self.buttons)

I’m just delaying the inevitable by calling createCombatButtons. No, actually, what I’m doing is expressing my intention, to create combat buttons. This is a legitimate step in programming and it tends to generate better code and to focus the mind.

For now, I’ll just jam these guys in above the arrow controls, recognizing that Dave and Bri need me to do something better.

function GameRunner:createCombatButtons(buttons)
    table.insert(buttons, self:createFleeButton())
    table.insert(buttons, self:createFightButton())
end

We really can’t put this off much longer–can we? Truth is, when I’m working like this, it seems to go best when I put things off longer and longer. Sooner or later, however, we have do do things.

I just guessed here, because I want to get something on screen.

function GameRunner:createFleeButton()
    local img = self:createFleeImage()
    return Button(100,300, 64,64, img)
end

function GameRunner:createFightButton()
    local img = self:createFightImage()
    return Button(300,300, 64,64, img)
end

Now I need the images. Let’s go simple first:

function GameRunner:createFleeImage()
    local img = image(64,64)
    setContext(img)
    fill(255,0,0)
    rectMode(CORNER)
    rect(0,0,64,64)
    setContext()
end

function GameRunner:createFightImage()
    local img = image(64,64)
    setContext(img)
    fill(0,255,0)
    rectMode(CORNER)
    rect(0,0,64,64)
    setContext()
end

I realize I didn’t give them names.

function GameRunner:createFleeButton()
    local img = self:createFleeImage()
    return Button("flee", 100,300, 64,64, img)
end

function GameRunner:createFightButton()
    local img = self:createFightImage()
    return Button("fight", 300,300, 64,64, img)
end

I actually rather expect buttons to show up. My expectations, as so often happens, are dashed:

Button:21: expect userdata, got nil
stack traceback:
	[C]: in function 'sprite'
	Button:21: in method 'draw'
	GameRunner:187: in method 'drawButtons'
	GameRunner:180: in method 'draw'
	Main:30: in function 'draw'

Yes, well, we really ought to return the image, having drawn it and all:

function GameRunner:createFleeImage()
    local img = image(64,64)
    setContext(img)
    fill(255,0,0)
    rectMode(CORNER)
    rect(0,0,64,64)
    setContext()
    return img
end

function GameRunner:createFightImage()
    local img = image(64,64)
    setContext(img)
    fill(0,255,0)
    rectMode(CORNER)
    rect(0,0,64,64)
    setContext()
    return img
end

I’m getting a vague sense of duplication here. But we’re just sketching so far. Let’s see if this runs:

buttons

There they are. And if we touch them, of course, we crash, because player doesn’t know how to do fight or flee.

Let’s see about making them better.

I’ll move them fully above the blue arrows, and let’s make them the same color and put words in them.

buttons2

That’s done with this:

function GameRunner:createFleeButton()
    local img = self:createFleeImage()
    return Button("flee", 100,350, 64,64, img)
end

function GameRunner:createFightButton()
    local img = self:createFightImage()
    return Button("fight", 200,350, 64,64, img)
end

function GameRunner:createFleeImage()
    local img = image(64,64)
    setContext(img)
    fill(109, 206, 227)
    rectMode(CORNER)
    rect(0,0,64,64)
    fill(0)
    textMode(CENTER)
    fontSize(20)
    text("Flee", 32,32)
    setContext()
    return img
end

function GameRunner:createFightImage()
    local img = image(64,64)
    setContext(img)
    fill(109, 206, 227)
    rectMode(CORNER)
    rect(0,0,64,64)
    fill(0)
    textMode(CENTER)
    fontSize(20)
    text("Fight", 32,32)
    setContext()
    return img
end

That works. Commit: inoperable fight and flee buttons.

Now let’s make this code right, reasoning that it is at least close to what we want. We see what we might call substantial duplication.

Convert constant to parameter here:

function GameRunner:createFleeImage()
    local img = image(64,64)
    setContext(img)
    fill(109, 206, 227)
    rectMode(CORNER)
    rect(0,0,64,64)
    fill(0)
    textMode(CENTER)
    fontSize(20)
    text("Flee", 32,32)
    setContext()
    return img
end

That gives us:

function GameRunner:createFightImage(label)
    local img = image(64,64)
    setContext(img)
    fill(109, 206, 227)
    rectMode(CORNER)
    rect(0,0,64,64)
    fill(0)
    textMode(CENTER)
    fontSize(20)
    text(label, 32,32)
    setContext()
    return img
end

Which we must call like this:

function GameRunner:createFightButton()
    local img = self:createFightImage("Fight")
    return Button("fight", 200,350, 64,64, img)
end

Still works just fine. So we can call that same function for our other button:

function GameRunner:createFleeButton()
    local img = self:createFightImage("Flee")
    return Button("flee", 100,350, 64,64, img)
end

All is still fine. Now we can remove createFleeImage, and rename createFightImage:

function GameRunner:createFleeButton()
    local img = self:createTextImage("Flee")
    return Button("flee", 100,350, 64,64, img)
end

function GameRunner:createFightButton()
    local img = self:createTextImage("Fight")
    return Button("fight", 200,350, 64,64, img)
end

function GameRunner:createTextImage(label)
    local img = image(64,64)
    setContext(img)
    fill(109, 206, 227)
    rectMode(CORNER)
    rect(0,0,64,64)
    fill(0)
    textMode(CENTER)
    fontSize(20)
    text(label, 32,32)
    setContext()
    return img
end

Still working. We note further duplication, in that createFightButton and createFleeButton have a certain subtle similarity. What is we were to use the string as the method called? Except we lower-case it, because we prefer lower-case instance methods?

function GameRunner:createCombatButtons(buttons)
    table.insert(buttons, self:createTextButton("Flee", 100,350))
    table.insert(buttons, self:createTextButton("Fight",200,350))
end

function GameRunner:createTextButton(label, x, y)
    local img = self:createTextImage(label)
    return Button(string.lower(label), x,y, 64,64, img)
end

Works. Delete createFightButton.

Now we do have some very magic numbers in there, the 64 and the 32, as well as the button coordinates. The code perhaps should not assume all buttons are 64x64, but for now it seems decent.

We could move all of this over to the Button class, and I think we should.

function Button:textButton(label, x, y, w, h)
    local w = w or 64
    local h = h or 64
    local img = Button:createTextImage(label,w,h)
    return Button(string.lower(label), x, y, w, h, img)
end

function Button:createTextImage(label, w, h)
    local img = image(w,h)
    setContext(img)
    fill(109, 206, 227)
    rectMode(CORNER)
    rect(0,0,64,64)
    fill(0)
    textMode(CENTER)
    fontSize(20)
    text(label, 32,32)
    setContext()
    return img
end

And now:

function GameRunner:createCombatButtons(buttons)
    table.insert(buttons, Button:textButton("Flee", 100,350))
    table.insert(buttons, Button:textButton("Fight",200,350))
end

Works, naturally. Commit: Text buttons moved to Button, and fight and flee still inoperable.

Now let’s make the buttons safe to press. Player needs fight and flee methods. Let’s have them do nothing for now, since we have no idea what they’ll actually do.

function Player:fight()
    
end

function Player:flee()
    
end

Commit: fight and flee buttons have null effect.

This is really rather clean. We started with some button creation in line, lots of duplication, then in small steps reduced the duplication, then moved the function over to where it seemed to belong: Button. But all this has given me an idea.

The fight/flee buttons are really intended to be alternatives, so it seems that the player has a “fight or flight” property, and that the buttons change that property. It would make sense, of a kind, if the currently-active state were reflected in the button’s brightness or color or something. This would lead us, perhaps, to a button that understood that it was attached to a property, as opposed to one that just sends a message.

I think it’s premature to do that, but we should keep it in mind.

Let’s back up one more level …

Glomming

We are currently glomming more and more odd capabilities into Player and Monster classes (and they are very similar, which is a clue in itself). We’re clearly just inches away from adding “fight or flight” to the long list of strength, health, speed, alive or dead, and who knows what all.

If you read one of the article series about game building out on the web, you’ll likely find a lot of design going on, with components inside the entities, wrappers for the entities, and all kinds of perfectly sensible structures. You’ll note that we’re emphatically not doing that.

My practice is to discover the need for additional structure and design, rather than to speculate about things that I anticipate that I’ll need. I’m not saying that I don’t think about those alternatives. Hell, we’re thinking about them right now. But I try, as a rule, not to put a structure in until I have a real need for it.

I’m not saying that you should do that, but I think if you try, and practice, you’ll find that if you keep the code clean, you can rather readily add design components as you need them, rather than having to think of them in advance. And that’s a good thing, in at least two regards:

  • First, we can’t possibly think of everything in advance, so if we rely on foresight, we’re sure to b blindsided;
  • Second, if we can reduce the effort we put into these structures before we get any real features, we can start delivering features sooner.

My assertion is that if we keep the code clean, we will usually be able to improve the structure incrementally, as we go. I’d like to say “always” rather than “usually”, but if I did, then I’d have to say “never do anything in anticipation”, and since sometimes I break that rule, I don’t want to state it.

But I have come to have great confidence that if the code is clean and clear, I’ll always be able to move it in whatever direction is needed. Perhaps, if you try the ideas, you’ll find the same thing. If you do, you’ll find that it leaves you sort of “relaxed”, in that you don’t need to worry about “will this work in the future”. You come to know that if it’s good design now, it’ll turn out to be readily improved as needed in the future.

Still, these objects are getting kind of messy, and I think something may have to be done about it soon.

So let’s talk about that, i.e. let’s speculate, or think. Just a bit. Not too much, we don’t want some Kent Beck or someone telling us to do an experiment. We’re just having a relaxing little design discussion, to fill our think tanks with a few ideas.

Bringing Order Out of Monster and Player Chaos

Looking at what Monsters and the Player do, we can see a few different “dimensions”.

They can display themselves
The monsters actually have a little animation that they run. If we had an animation for the player, she’d have one as well. Each of them knows how to display itself in a tile.
They can move
Each one knows how to move. Monsters have a couple of simple behaviors to decide whether to move and in which direction: random and toward player. We could imagine other behaviors, like patrolling or hiding. The player moves when someone presses her buttons. As it were.
They have properties
They have Health, Speed, Strength. They have associated behavior to manipulate these properties. They also have associated sounds, a cry when hurt and one when killed.
They have combat-related behavior
They know how to apply damage, how to report life or death. They also report out their key properties, a clue that perhaps too much combat behavior is going on outside the entities.
They’re long
Player is about 250 lines, and Monster about 300. Particularly in view of their commonalities, this seems rather a lot.

What might we do about this? Well, as GeePaw Hill advised in another context yesterday, we have patterns like Strategy, Adapter, and Observer to think about. We could imagine a movement Strategy plugged into an Entity that made it move like one or another kind of Monster, or like the Player.

We could imagine a combat Adapter that connects to an Entity, providing combat-related information and behavior.

Now I’ll freely grant that while at one point in my life I had a bunch of those famous Gang of Four patterns at my fingertips, that I don’t think in those terms much at all any more. My design sense is focus almost entirely on removal of duplication, and pushing behavior to places where it seems to belong, as we pushed the text button behavior into Button this morning.

Whether where I am is a good place, or a bad place, is hard to say. But I can tell you for sure, that had I not spent a long time studying the GoF patterns, and other patterns, and trying them in real and study code, I’d not be as good as I am. Maybe I’m great now, maybe merely adequate. Most likely the latter. But I’d be even lower down without putting in the time to learn those, and many other, things, until I had them pretty cold.

As for dealing with the Glomming, I feel that we’d do better if the Monsters and the Player had some of their commonalities factored out somehow, so that their code would converge, removing the duplication that we can sense when we read them, even though very few of their methods are identical.

Are any of their methods identical? Interesting question. They feel the same to me. What can we find?

Well, getTile, health, isAlive, isDead, strength are identical. The speed method should be, but the monsters aren’t using their speed value. Let’s fix that.

function Monster:speed()
    return 5
end

That becomes, of course:

function Monster:speed()
    return self.speedPoints
end

Let’s make sure they’re rolling up their speed points.

...
    self.healthPoints = self:roll(self.mtEntry.health)
    self.strengthPoints = self:roll(self.mtEntry.strength)
    self.speedPoints = self:roll(self.mtEntry.speed)
...

Yes, they are. A quick run lets me encounter two Yellow Widows, and to see that they have different speeds, so that works just fine.

Commit: monsters use rolled speed.

We were looking for duplication or similarity between Monster and Player.

The damageFrom method is identical. So is die. So is displayDamage. They both have displaySheet but the implementation is different. I could make them the same, trivially. They both implement distanceFromPlayer, though I’m not sure why player would ever need to know that. Who’s calling that?

Player:65: who called this?
stack traceback:
	[C]: in function 'assert'
	Player:65: in method 'distanceFromPlayer'
	AttributeSheet:20: in method 'draw'
	Player:88: in method 'drawExplicit'
	GameRunner:173: in method 'drawLargeMap'
	GameRunner:151: in method 'draw'
	Main:30: in function 'draw'

Ah! attribute sheets call it. They’re trying to decide whether to display. So that method could be made the same and amount to duplication at some tiny cost in simplicity.

The selectDamageTint method is the same.

So there is a raft of duplication going on here between Monster and Player. And you’ll have noticed that I often speak of them as “Entities”, because they’re so similar.

Subclassing is a card you can only play once.

An easy way to remove a lot of this duplication would be to build a superclass for the two of them, probably called “Entity”, and then move capability up to the superclass. I am reluctant to do that: Kent Beck has said that “Subclassing is a card you can only play once. Keep it in your hand as long as possible.”

I’m inclined to look for alternatives to subclassing, just because I know it’ll work OK in this case, and I’m confident that it will do no harm … and therefore I want to try something else, to see if it’s better.

Let’s see if we can classify those common methods a bit.

Property-related
health, strength, speed, isAlive, isDead. PossiblyaddPoints, which is player-only.
Location-related
‘getTile, 'distanceFromPlayer, maybe others
Display-related
draw, drawExplicit?, photo (different), selectDamageTint,
Combat
damageFrom, and the property-related ones above.

This breakout brings me to an issue. Suppose we were to decide to break out an Attributes member variable, pointing to some kind of value-bearing object that holds on to the health and such points, and perhaps the inventory as well.

Now in some people’s code, you’d find code like this when someone wanted to know about a monster’s health:

local h = monster.attributes.healthPoints
...

You won’t find that in my code, or at least you won’t when I’m on my game. You might find:

local h = monster:health()
...
function Monster:health()
    return self.attributes:health()
end

function Attributes:health()
    return self.healthPoints
end

I’m not going to rip out the guts of Monster, pulling the attributes table through the incision and then picking out the member variable. I’m going to cal through to the object that I have in hand, namely the monster itself.

I am also not big on pure value objects, although I’m happy to use a few, such as vec2 when they work for me. But I like my objects to have behavior, not to pack up a record, then take it apart and manipulate it somewhere else. That’s not good design by my standards.

This does mean that adding a contained component such as a properties sheet doesn’t narrow the interface of the original object, which makes the change less compelling, although it might give us a bit more clarity when we fold in point name look up and the like. It also makes subclassing seem more valuable.

Another concern with subclassing is that our superclass would certainly include some concrete methods. It might also include some methods that must be overridden or provided, so it would be part implementation and part interface. At the scale of what we’re doing here, I think I’m OK with that.

I can imagine some plug-in behavior, such as for motion of monsters, and if we did that we could probably plug in the player’s touch motion as another component. But that’s all very speculative.

I’ll tell you what, though. All this thinking has made me want to go ahead and do the superclass thing. I’ll leave it to my readers to object if they see something better. Let’s get started:

-- Entity
-- RJ 20210201 - superclass for Monster and Player

Entity = class()

-- Monster
-- RJ 202012??

Monster = class(Entity)

-- Player
-- RJ 20201205

Player = class(Entity)

So that works, no surprise there. Let’s move up some of that duplicated stuff.

Here’s one:

function Monster:damageFrom(aTile,amount)
    if not self.alive then return end
    self.healthPoints = self.healthPoints - amount
    if self.healthPoints <= 0 then
        sound(self.deathSound, 1, self.pitch)
        self:die()
    else
        sound(self.hurtSound, 1, self.pitch)
    end
end

function Player:damageFrom(aTile,amount)
    if not self:isAlive() then return end
    self.healthPoints = self.healthPoints - amount
    if self.healthPoints <= 0 then
        sound(self.deathSound, 1, self.pitch)
        self:die()
    else
        sound(self.hurtSound, 1, self.pitch)
    end
end

So:

function Entity:damageFrom(aTile,amount)
    if not self:isAlive() then return end
    self.healthPoints = self.healthPoints - amount
    if self.healthPoints <= 0 then
        sound(self.deathSound, 1, self.pitch)
        self:die()
    else
        sound(self.hurtSound, 1, self.pitch)
    end
end

And the other two methods are removed. Commit: folding damageFrom up into Entity.

Let’s do a few more, the attribute ones:

function Monster:health()
    return self.healthPoints
end

function Monster:isAlive()
    return self.alive
end

function Monster:isDead()
    return not self:isAlive()
end

function Monster:speed()
    return self.speedPoints
end

function Player:strength()
    return self.strengthPoints
end

All these get moved to Entity. Game works, Commit: move health speed, alive, and strength to Entity.

That last maneuver removed 38 lines, and added 20, netting 18 lines. Nothing to sneeze at. Maintain social distancing.

Let’s look at a couple more. I think getTile is a candidate:

function Monster:getTile()
    return self.tile
end

function Player:getTile()
    return self.tile
end

Yep. Done.

And there’s this:

function Monster:distanceFromPlayer()
    return self.runner:playerDistance(self.tile)
end

function Player:distanceFromPlayer()
    return 0
end

We can leave these or move the Monster one up and let Player use it. It’ll be slightly slower but not much. Let’s do it.

Now how does the runner do that, I wonder:

function GameRunner:playerDistance(aTile)
    return self.player:distance(aTile)
end

function Player:distance(aTile)
    return aTile:distance(self.tile)
end

Now it happens that Monster doesn’t implement that distance method. But it does no harm to provide it, and it might do some good. Let’s move it up. We might need to know how far something is from a monster someday, and it kind of makes more sense to have it up there in the common code.

Commit: move distance, distanceFromPlayer, to Entity.

OK, let’s call it a morning and sum up, since it’s 1300 hours, which is pretty late morning-wise. But we were having fun.

Summary

We popped a couple of buttons into the player setup, and generalized them enough to remove most of the duplication. They’re wired up to empt methods for now, but my theory is they’ll come in handy when we take our next cut at combat. I have a bit more reading to do before I have a sense of what that next cut will be.

Then we talked about the observation that the Player and Monster classes are getting to be full of stuff, and at least considered some options. In the end, I created a superclass and moved some common functionality into it, in one case accepting the more general version from Monster as the same method for Player, although we could have left the different implementations in place.

The entity class is now 47 lines long, so we have probably gained a net reduction in lines of about 40. Not all bad.

Is this a good thing to do? At present, I can see no downside. These two objects, Monster and Player, have much in common, and expressing that commonality is a good thing. We’ll see where it goes, and whether we get in any kind of trouble, but I really don’t expect that we will.

All in all, a very gentle session with no surprises. A good way to start a new week and a new month.

See you next time!


D2.zip