Dungeon 131
Let’s do some work on inventory items and touch to use. That should be amusing in its fashion.
I was reading something about Python last night, saying that the language and its ecosystem have become quite powerful and useful. And since Bryan of the Zoom Ensemble is writing his dungeon program in Python, I get to see some of that up close, including its very nice collection handling, closures, and PyCharm, with some very useful refactoring capability.
Heck, I’d pay extra for a better interface to my many tabs, one for each class, and a few for tests. Refactoring would be luxury. But there are things I like about Codea Lua as well.
First of all, its spare simplicity lets me see, and show, programming and style issues very clearly. There’s not a lot of room to hide in Lua. Second, I personally enjoy editing and refactoring “by hand” rather than turning a tool loose to run rampant through the code. I prefer the steady pace of typing. Finally1, I like that when I need to, I can usually reach inside Lua and make it do something that I really need, or that I just want to experiment with.
In my many years programming, I’ve found that the main issues we encounter are people issues, and people are the same (and different) no matter what language they use. And the technical issues, which are most of what we deal with here, seem mostly to be independent of language. Oh, this one has static typing, and that one is dynamic, this one has reflection and that one doesn’t. The balance changes from language to language, but the issues of expressing ideas and organizing the expression seem to me to be mostly the same.
But there I go again, philosophizing when I could be coding.
Inventory Menu
The overall plan is that some items, when we pick them up, will go into an “inventory” rather than apply instantly. You can pick up a can of room freshener and use it later in another room. (Hey, that might be an idea …)
My working plan is to show little icons at the bottom of the player’s Attribute sheet, and to make them touch-sensitive. Whether they’ll deploy instantly upon touch, I’m not certain. If it seems too hard to get the touch right (they’re pretty small), we may have to have a confirmation window or something. Let’s hope not. Before I did that, I’d come up with some other idea for the inventory.
And there are other possibilities we should consider. The Player stays centered in the screen now.
It might be better to put the inventory items in a row across the top of the screen, or on the side above the buttons. In fact, I like the top of screen idea so well, I think I’ll start with that.
I reckon it won’t make much difference. Imagine a sort of generic rectangular container that can hold a bunch of icons representing inventory. We can position that rectangle anywhere, up at the top, or in front of the attribute sheet, or, well, anywhere.
I had been thinking to associate inventory closely with the attribute sheet. This little conversation tells me not to do that, but to instead build a separate inventory display that can be positioned wherever I want it.
I’m glad we had this little chat. This is why I recommend thinking before, during, and after doing things.
Moving Forward
The game’s tiles, on the iPad’s screen (as interpreted by Codea) are 64x64. That’s a nice size, should be easy enough to touch. Let’s suppose that the inventory menu will also be that size. The game screen on the iPad is 85 tiles wide. We surely don’t need 85 inventory items. Let’s leave room for, oh, 20ish.
What will our inventory thingie have to do? It’ll display the items, it’ll detect touches, and, somehow, it’ll want to dispatch the touch to the item under your finger. I think, for now, I’ll imagine some kind of InventoryItem class or protocol or something, and an InventoryViewer that has a collection of the items.
I’‘m going to try to create at least some tests for the viewer, though its display will have to be tweaked on screen.
How can I start? I think I’ll make a simple InventoryItem class that holds a sprite name, since we have sliced out a number of nice icons. And an object Inventory that can add and remove them? Should we allow only one of a given name? No, let’s just add whatever we’re given. We’ll deal with details as they arise.
_:test("add items to inventory", function()
local i1 = InventoryItem("green_staff")
local i2 = InventoryItem("snake_staff")
_:expect(Inventory:count()).is(0)
Inventory:add(i1)
_:expect(Inventory:count()).is(1)
Inventory:add(i2)
_:expect(Inventory:count()).is(2)
end)
That’s just a little scenario I imagined. I’m not sure we’ll want count, but I think we will, as we’ll see soon. To make that run:
Inventory = class()
local inventory = {}
function Inventory:init()
assert(false, "do not instantiate inventory")
end
function Inventory:add(item)
table.insert(inventory, item)
end
function Inventory:count()
return #inventory
end
And we’ll want the item:
InventoryItem = class()
function InventoryItem:init(icon)
self.icon = icon
end
The test should run. And it does. Not much of a test but it gets us going. Let’s get the inventory to display. What if GameRunner just told Inventory to display?
function GameRunner:draw()
font("Optima-BoldItalic")
self:drawLargeMap()
self:drawButtons()
self:drawTinyMap()
self:drawMessages()
self:drawInventory()
end
function GameRunner:drawInventory()
Inventory:draw()
end
That seems easy enough. Let’s do the draw. For now, I’ll ensure that it has something to draw.
After some moderately grotesque hackery, I have this:
Inventory = class()
local inventory = {}
function Inventory:init()
assert(false, "do not instantiate inventory")
end
function Inventory:clear()
inventory = {}
end
function Inventory:add(item)
table.insert(inventory, item)
end
function Inventory:count()
return #inventory
end
function Inventory:draw()
pushMatrix()
pushStyle()
rectMode(CENTER)
spriteMode(CENTER)
if self:count() == 0 then
self:add(InventoryItem("snake_staff"))
self:add(InventoryItem("green_staff"))
self:add(InventoryItem("skull_staff"))
self:add(InventoryItem("knob_staff"))
self:add(InventoryItem("cudgel_staff"))
end
local drawPos = self:startingPos()
for i,item in ipairs(inventory) do
item:draw(drawPos)
drawPos.x = drawPos.x + 64
end
popStyle()
popMatrix()
end
function Inventory:startingPos()
return vec2(WIDTH/2-64*self:count()/2 + 64, HEIGHT-70 + 32)
end
InventoryItem = class()
function InventoryItem:init(icon)
self.icon = icon
end
function InventoryItem:draw(pos)
fill(136,129,107)
rect(pos.x,pos.y,64,64)
sprite(Sprites:sprite(self.icon), pos.x,pos.y, 40)
end
The result is this picture:
Now that we have it on the screen and centered, let’s see about normalizing some of those numbers.
local ItemWidth = 64
function Inventory:startingPos()
return vec2(ItemWidth+WIDTH/2-ItemWidth/2*self:count(), HEIGHT-ItemWidth/2)
end
Well, that’ll have to do for now. Centering is weird, but this works as I intended.
Let’s mix up the icons a bit, since we have others.
if self:count() == 0 then
self:add(InventoryItem("snake_staff"))
self:add(InventoryItem("blue_bottle"))
self:add(InventoryItem("skull_staff"))
self:add(InventoryItem("green_flask"))
self:add(InventoryItem("gold_bag"))
end
That looks nice:
Moving Forward?
I’m a bit muzzy this morning: I think maybe the vaccine is hitting me. I know I haven’t been on the sauce. Anyway, I think the next steps will be to break out the display into a view of some kind, and to use that code to provide the “addresses” of the InventoryItems, and to use that with touch to trigger the item’s action.
But I think I’ll wrap for today before I do serious damage.
See you next time!
-
Not really finally. There are many things to like. I could go on … and on … ↩