While I’m in another world, let me muse about Content.

I think we’ll approach pluggable behavior in Content as a way to reduce the number of classes while adding capability. In particular, I think we will start with interact_with_player, since we have a couple of different cases now and need at least one more change: we’d like for the Switch to refuse entrance, so that Dot can bump it to change it but she can’t step on it. We’ll have to do things to be certain it can’t block a path but that can be done manually until we have smarter content placement.

We’ll want a similar thing with the Chest, I think. Probably you can’t step on it unless it is open and empty, or something like that.

For that to work, we need a way for player interaction to return an indicator as to whether the player can enter the cell in question. There can be more than one content object in a cell and presumably if any of them wants to refuse entry, we can’t enter. (There may be monsters or NPCs in a cell as well. At this writing we don’t know if those are Content or something else.)

Here’s the relevant code in Dungeon:

class Dungeon:
    def place_player_at(self, cell):
        self.player_cell = cell
        for item in list(self.contents_at(cell)):
            item.interact_with_player(self)

Ideally, all interact_with_player implementations would return whether or not the player can enter, perhaps True if it’s OK, False if it’s not.

Only Content objects can be in there now, so let’s change the signature in the ABC and demand it of the implementations. Done, not very interesting to read. Example:

class AppearingContent(Content):
    def interact_with_player(self, dungeon) -> bool:
        if self.visible:
            dungeon.receive_item(self)
            dungeon.announce(f'You have received {self.name}!')
        return True