Improving the Content
A few little changes to content this morning trigger some further improvement this afternoon. It’s better than it was, and more capable too.
I want to continue to make this content blob more nearly right. In a spirit of making things worse so as to see better how to improve them, let’s have two buttons and two hidden items.
Clearly the buttons will need to send different messages, and the items subscribe differently. Let’s have the buttons prepend their name to the message.
main.py
dungeon.place_content_at(Cell(34, 28), Button('key'))
dungeon.place_content_at(Cell(36, 28), SecretKey('a secret key'))
dungeon.place_content_at(Cell(34, 29), Button('torch'))
dungeon.place_content_at(Cell(36, 28), SecretKey('a brilliant torch'))
I need the SecretKey to be more clever. I’ll rename it to SecretObject, add a resource. I was going to add a short name but for now let’s make the Button deal with it:
dungeon.place_content_at(Cell(34, 28), Button('a secret key'))
dungeon.place_content_at(Cell(36, 28), SecretKey('a secret key'))
dungeon.place_content_at(Cell(34, 29), Button('a brilliant torch'))
key = SecretKey('a brilliant torch', my_resources + 'Torch.png')
dungeon.place_content_at(Cell(36, 29), key)
SecretKey needs too honor its incoming resource:
class SecretKey:
def __init__(self, name='a secret key', resource=None):
if resource is None:
resource = '/Users/ron/Desktop/DungeonTiles/png/objects/Key (1).png'
self.name = name
self.painter = ContentPainter(resource, scale=0.5)
self.set_visibility(False)
And it needs to listen for its name and button_pressed:
class SecretKey:
def placed(self, dungeon):
self.set_visibility(False)
def callback(event):
dungeon.announce(f'{self.name} has appeared!')
self.set_visibility(True)
dungeon.subscribe_once(self.name + ' button_pressed', callback)
And Button needs to prepend its name:
class Button:
def interact_with_player(self, dungeon):
dungeon.publish(self.name + ' button_pressed')
That’s sufficient to test, I think. Works as intended! Two buttons appear, Dot steps on them both, two different things appear, and she gets the different messages.

There is a small issue. The object names start with the desired article, ‘a’ in this case, but that means that the appearance message is “a secret key has appeared!”, starting in lower case. I think I’ll let that slide for now.
OK, let’s rename SecretKey to AppearingContent. And we’ll require that the use provide the resource instead of defaulting it.
dungeon.place_content_at(Cell(34, 28), Button('a secret key'))
appearing = AppearingContent('a secret key', my_resources + 'Key (1).png')
dungeon.place_content_at(Cell(36, 28), appearing)
dungeon.place_content_at(Cell(34, 29), Button('a brilliant torch'))
appearing = AppearingContent('a brilliant torch', my_resources + 'Torch.png')
dungeon.place_content_at(Cell(36, 29), appearing)
You may have noticed the scaling issue in the picture above: the torch is pretty large. I am not sure why that is happening. A quick print tells me that I’m getting negative values for the y size of ll the textures. This one is narrow so it is particularly bad. I have a suspicion that Pillow, whatever that is, uses upside-down coordinates from those of Arcade. I buy insurance by using abs:
def scale_texture(texture, adjust=1):
pil = texture.image
x0, y1, x1, y0 = pil.getbbox()
x = abs(x1 - x0)
y = abs(y1 - y0)
size = x if x > y else y
return cell_size / size * adjust
Now the torch is, if anything, too small:

I think we’ll want to deal with that in a more general fashion, when we build an asset-holding object. In that object, I think we’ll want the ability to set an individual scale factor on each item. We’ll see. Small issue, left for later.
Reflection
We have added some capability: smarter buttons and a general-purpose appearing item that listens for buttons. Things are still too tightly linked. For example, the AppearingItem listens for a key string plus “button_pressed”. We should probably give the same message explicitly to the button and the item, and probably there will be other trigger items besides the button. Perhaps stepping into the water unleashes a school of piranhas. Or something.
But this is our way: we start with something specific, and then as additional needs become visible, we extend the objects and refactor toward a better design. For me, this seems to work better. When I design a bunch of objects without concrete examples first, I tend to get it wrong.
Arguably, this whole recent exercise with Content is a result of building too much capability without clear examples. Or, it might just be an ordinary example of how things go: the design is good, we extend it, it starts getting weird, and we bring it back toward good.
I think that we can begin to see the future a bit here. The ContentPainter probably really wants to be a SpriteView or ContentView, and it wants not to be owned by Content, but to own the content, as a view holds the model, not the other way around. We probably want to use PubSub to communicate between the Dungeon and the DungeonView. And with any luck at all, we’ll be able to simplify the Content abstract class and its subclasses.
And then we’ll get into more trouble, perhaps with some active objects, like Monsters, and Non-Player Characters. And then we’ll get out of trouble again. Rinse, repeat.
See you next time!