Hello, loves!

I’ve thought of what may be a fun idea for the game. Let’s talk about it, maybe do some of it. Spike written, thought about, tossed.

We’ve been working on paths and thinking about making sure that tools are on the same side of obstacles as Dot, so that she can be sure of having what she needs to traverse the dungeon. So I was thinking about a door.

Suppose there is a door requiring the red key. The door is red.

     Dot
<bumps door>

     Door
There is a locked door here.

     Dot
<bumps door>

     Door
There is a locked red door here.

     Dot
<bumps door>

     Door
There is a locked red door here with a red keyhole.

     Dot
<bumps door>

     Door
You need a red key to open this locked red door with a red keyhole.

     Dot
<bumps door>

     Door
<creates path to key>
Follow these Red Breadcrumbs to the Red Key to open this locked Red Door with a Red Keyhole.

I think that sequence would not often occur, but when it did, it might possibly be amusing.

So let’s see what we can do in support of that kind of idea.

As always, we’d like to move in small steps.

How might this work? We have the ability to find a path between any two mutually-accessible points, so we can get a list of cells that should contain breadcrumbs. (As far as I know, we do not have dungeon art for breadcrumbs, but we’ll not worry about that in the demo.)

We could add the crumbs to the relevant cells as contents. Their interaction behavior could be to remove themselves. (I’m not sure we can do that without giving them to Dot, but that is easy fixed. Or maybe they are useful later, to feed the bird. Who knows?)

As a spike, let’s implement the K key to create content of some kind on every cell on the path from Dot to the existing red key in the dungeon (it looks more orange to me but we’ll also not worry about that).

The use of the key will be temporary but it seems to me to be about the right size.

class DungeonView:
        elif symbol == arcade.key.K:
            self.draw_breadcrumbs()

We have no such method of course, nor is the view the place to do it.

class DungeonView:
    def draw_breadcrumbs(self):
        self.dungeon.draw_breadcrumbs()

This is progress, believe it or not.

Time passes … More time passes. Finally, with more fumbling than one might prefer, I have this picture:

Dungeon map showing path of skulls from Dot to red key

The path of skulls leads from Dot to the red key, more or less ad advertised.

I have this code, which doesn’t quite work:

class Dungeon:
    def draw_breadcrumbs(self):
        crumb = ContentFactory().decor(name="crumb",
                                       resource='/Users/ron/Desktop/DungeonTiles/png/objects/Skeleton1.png',
                                       scale=0.5)
        origin = self.player_cell
        target = origin
        paths = dict()
        for cell, value in (Flooder(layout=self.layout, origin=origin)
                .can_traverse()
                .initial_value(None)
                .next_value(lambda c, v: c)
                .flood()):
            paths[cell] = value
            contents = self.contents[cell]
            for item in contents:
                print(f'found {item.name}')
                if item.name == 'a red key':
                    target = cell
                    break
        print(f'{target=} {origin=}')
        while target is not None:
            print(f'adding to {target}')
            self.place_content_at(target, crumb)
            self.publish('add_content', 'crumb', cell=target, item=crumb)
            target = paths[target]

The first problem that I encountered wasn’t with this code, complex though it is. The print told me that we were correctly adding the content. The issue was that the content sprite lists are created as the dungeon starts and there was no provision for adding things later. That has to be done in the view, so I had Dungeon subscribe to a new message ‘add_content’:

class DungeonView:
    def subscribe_to_add_content(self, dungeon):
        def callback(*, pub_sub, cell, item):
            print('add content')
            view = ContentView(cell, item, item.resources, item.scale)
            sprite = view.sprite
            sprite.position = cell.center_position(cell_size)
            sprite.visible = True
            self.content_views[item] = view
            self.content_views_by_room[cell].append(view)
            self.content_sprite_list.append(sprite)
        dungeon.subscribe('add_content', '', callback)

Having written that method, I forgot to call it in the setup, so that held me back for a while. However, it still doesn’t work, and I do not know why. So I used a bigger hammer:

class DungeonView:
    def draw_breadcrumbs(self):
        self.dungeon.draw_breadcrumbs()
        self.create_content_lists()

I added the self.create_content_list(), completely rebuilding the entire content lists of the dungeon, which does sort of work, except that when we create the content lists we set everything to invisible, so Dot has to walk around a bunch to get all the skulls to show up for their photo, even though they were there and with that area illuminated already, I would expect them to show up right away. Certainly that’s what I want.

Summary

So what we have here is a classic spike, the original metaphor being driving a big spike through a board. We have slammed code through the system and obtained something like what we want, and we have learned some key things:

  • The code for finding the path works and probably needs to be encapsulated somewhere as a general utility for finding things.
  • Adding the content and rebuilding the content sprite list causes all the crumbs to be present and drawable, but has the effect of forgetting what is already visible, having been lighted.
  • Rebuilding the content sprite list breaks appearing objects by setting their states back to initial.
  • It is supposedly OK to add sprites dynamically, but the code shown above does not work, though it appears that it should.

I think the next thing I’d like to do, after we toss this spike in the trash, is to work on how to add a sprite and make it visible at once, and how to get that to happen when we add a content item.

A related issue will have to do with illumination. If I’m not mistaken, we set sprites to visible when we move Dot. Yes:

class ContentView:
    def illuminate(self, dot, range):
        if dot.manhattan_distance(self.cell) < range:
            self.sprite.visible = True

So we’ll have to work up a way to “inherit” visibility when we add content dynamically.

And surely other issues will come up.

In essence the lesson of this spike appears to be “Doable, but we need some detailed information about arcade and some new low-level capabilities in our own code before it’s production-ready”.

The conventional wisdom for spikes is that the code should be thrown away. I do not always follow that advice, when the spike goes particularly well and looks to be able to be refactored readily to something desirable. I think today we’ll throw it away and do better next time.

Reset. See you next time!