Hello, loves!

Let’s move on_key_press out of DungeonView. Down to 146 lines, from 226 just a few commits ago.

I think I’ll just do it unless something interesting comes up. Here it is going in:

    def on_key_press(self, symbol: int, modifiers: int) -> bool | None:
        if self.key_lock is not None:
            return
        self.key_lock = symbol
        if symbol == arcade.key.RIGHT:
            self.move_player(Direction.EAST)
        elif symbol == arcade.key.LEFT:
            self.move_player(Direction.WEST)
        elif symbol == arcade.key.UP:
            self.move_player(Direction.NORTH)
        elif symbol == arcade.key.DOWN:
            self.move_player(Direction.SOUTH)
        elif symbol == arcade.key.F:
            self.dungeon.maker_flood()
        elif symbol == arcade.key.B and (modifiers & arcade.key.MOD_CTRL):
            message = 'You have been eaten by a breakpoint!'
            self.pub_sub.publish('announce', 'view', message=message)
            pass
        elif symbol == arcade.key.H:
            self.dungeon.make_initial_announcement()
        elif symbol == arcade.key.K:
            self.dungeon.show_path_to('a red key', self)
        else:
            self.key_lock = None

    def on_key_release(self, symbol: int, modifiers: int) -> bool | None:
        if symbol == self.key_lock:
            self.key_lock = None

Basically if a key is down, we ignore any other keys. When a key goes down, we dispatch and do stuff. The new object would like to be started with a DungeonView, and let’s pass in the dungeon, in case we want to do something cute with testing.

I’ll just replicate that in a class, instantiate it, and forward to it. Or, wait, how can we arrange that it gets the key-presses its own self? That will take a bit of research, we’ll stick with plan A.

class DungeonView(arcade.View):
    def __init__(self, dungeon, testing=False):
        if not testing:
            super().__init__()
        self.dungeon = dungeon
        self.pub_sub = dungeon.pub_sub
        self.subscribe(dungeon, self.pub_sub)
        self.setup_assets()
        self.keyed_floor_sprites = KeyedSpriteList(arcade.SpriteList())
        self.content_views: dict[Content, ContentView] = dict()
        self.content_views_by_cell: dict[Cell, list[ContentView]] = defaultdict(list)
        self.content_sprite_list = None
        self.cameras = None
        self.keys = KeyPress(self, self.dungeon, self.pub_sub)

    def on_key_press(self, symbol: int, modifiers: int) -> bool | None:
        self.keys.on_key_press(symbol, modifiers)

    def on_key_release(self, symbol: int, modifiers: int) -> bool | None:
        self.keys.on_key_release(symbol, modifiers)


class KeyPress:
    def __init__(self, view, dungeon, pub_sub):
        self.view = view
        self.dungeon = dungeon
        self.pub_sub = pub_sub
        self.key_lock = None

    def on_key_press(self, symbol: int, modifiers: int) -> bool | None:
        if self.key_lock is not None:
            return
        self.key_lock = (symbol, modifiers)
        if symbol == arcade.key.RIGHT:
            self.view.move_player(Direction.EAST)
        elif symbol == arcade.key.LEFT:
            self.view.move_player(Direction.WEST)
        elif symbol == arcade.key.UP:
            self.view.move_player(Direction.NORTH)
        elif symbol == arcade.key.DOWN:
            self.view.move_player(Direction.SOUTH)
        elif symbol == arcade.key.F:
            self.dungeon.maker_flood()
        elif symbol == arcade.key.B and (modifiers & arcade.key.MOD_CTRL):
            message = 'You have been eaten by a breakpoint!'
            self.pub_sub.publish('announce', 'view', message=message)
            pass
        elif symbol == arcade.key.H:
            self.dungeon.make_initial_announcement()
        elif symbol == arcade.key.K:
            self.dungeon.show_path_to('a red key', self.view)
        else:
            self.key_lock = None

    def on_key_release(self, symbol: int, modifiers: int) -> bool | None:
        if (symbol, modifiers) == self.key_lock:
            self.key_lock = None

This seems to work just fine. Commit: moved on_key_press to KeyPress object.

A bit of research finds that this incantation tells me whether there is an active window, so that tests can deal with KeyPress (and DungeonView) without creating a window.

class KeyPress(arcade.View):
    def __init__(self, view, dungeon, pub_sub):
        if arcade.window_commands._window:
            super().__init__()
        self.view = view
        self.dungeon = dungeon
        self.pub_sub = pub_sub
        self.key_lock = None

I thought this would allow me to remove the on_key_press and on_key_release methods from DungeonView. Further research tells me that only the active view receives key presses. so we’ll need to leave the forwarding in. No biggie. Reset.

Summary

So that went swimmingly, which I think means “well”, and the better check for whether there is a window is useful. I’ve updated the calling sequence for DungeonView and its creators. DungeonView is down to a positively healthy 156 lines, from 226 just a few sessions ago. I think we can do better. Possibly we will.

Down to 146: removed unused draw_passages, which doesn’t work as constituted anyway.

See you next time!