from __future__ import annotations from typing import List, Optional, TYPE_CHECKING import tcod import colors from actions import ( BaseAction, QuitAction, BumpAction, WaitAction, PickupAction, ) if TYPE_CHECKING: from engine import Engine #input options MOVE_KEYS = { #arrow keys tcod.event.KeySym.UP: (0, -1), tcod.event.KeySym.DOWN: (0, 1), tcod.event.KeySym.LEFT: (-1, 0), tcod.event.KeySym.RIGHT: (1, 0), tcod.event.KeySym.HOME: (-1, -1), tcod.event.KeySym.END: (-1, 1), tcod.event.KeySym.PAGEUP: (1, -1), tcod.event.KeySym.PAGEDOWN: (1, 1), #numpad keys tcod.event.KeySym.KP_1: (-1, 1), tcod.event.KeySym.KP_2: (0, 1), tcod.event.KeySym.KP_3: (1, 1), tcod.event.KeySym.KP_4: (-1, 0), tcod.event.KeySym.KP_6: (1, 0), tcod.event.KeySym.KP_7: (-1, -1), tcod.event.KeySym.KP_8: (0, -1), tcod.event.KeySym.KP_9: (1, -1), #vi key mapping tcod.event.KeySym.h: (-1, 0), tcod.event.KeySym.j: (0, 1), tcod.event.KeySym.k: (0, -1), tcod.event.KeySym.l: (1, 0), tcod.event.KeySym.y: (-1, -1), tcod.event.KeySym.u: (1, -1), tcod.event.KeySym.b: (-1, 1), tcod.event.KeySym.n: (1, 1), } WAIT_KEYS = { tcod.event.KeySym.PERIOD, tcod.event.KeySym.KP_5, tcod.event.KeySym.CLEAR, } PICKUP_KEYS = { tcod.event.KeySym.COMMA, } CURSOR_SCROLL_KEYS = { tcod.event.KeySym.UP: -1, tcod.event.KeySym.DOWN: 1, tcod.event.KeySym.PAGEUP: -10, tcod.event.KeySym.PAGEDOWN: 10, tcod.event.KeySym.KP_2: 1, tcod.event.KeySym.KP_8: -1, } CURSOR_CONFIRM_KEYS = { tcod.event.KeySym.RETURN, tcod.event.KeySym.SPACE, } #the event handlers are one part of the engine class EventHandler(tcod.event.EventDispatch[BaseAction]): engine: Engine def __init__(self, engine: Engine): super().__init__() self.engine = engine def render(self, console: tcod.console.Console) -> None: pass #no-op #callbacks def ev_quit(self, event: tcod.event.Quit) -> Optional[BaseAction]: return QuitAction() def handle_events(self, context: tcod.context.Context) -> bool: """If any Action signals True, then the game state should be progressed after this""" result = False for event in tcod.event.wait(): context.convert_event(event) #adds mouse position info action = self.dispatch(event) if action is None: continue result |= action.perform() return result class GameplayHandler(EventHandler): def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[BaseAction]: key = event.sym #SDL stuff, neat. player = self.engine.player #player input if key == tcod.event.KeySym.ESCAPE: return QuitAction() if key in MOVE_KEYS: xdir, ydir = MOVE_KEYS[key] return BumpAction(player, xdir = xdir, ydir = ydir) if key in WAIT_KEYS: return WaitAction(player) if key in PICKUP_KEYS: return PickupAction(player) if key == tcod.event.KeySym.o: #TODO: temove this self.engine.event_handler = OptionSelector(self.engine, self, ["zero", "one", "two", "three"], lambda x: print("You chose", x)) #TODO: remove this if key == tcod.event.KeySym.BACKQUOTE: #lowercase tilde self.engine.event_handler = LogHistoryViewer(self.engine, self) if key == tcod.event.KeySym.TAB: # self.engine.event_handler = InventoryViewer(self.engine, self) #TODO: deal with this def ev_mousemotion(self, event: tcod.event.MouseMotion) -> None: if self.engine.floor_map.in_bounds(event.tile.x, event.tile.y): self.engine.mouse_location = event.tile.x, event.tile.y class GameOverHandler(EventHandler): """Game over, man, GAME OVER!""" def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[BaseAction]: key = event.sym #SDL stuff, neat. #player input if key == tcod.event.KeySym.ESCAPE: return QuitAction() if key == tcod.event.KeySym.BACKQUOTE: #lowercase tilde self.engine.event_handler = LogHistoryViewer(self.engine, self) return None class LogHistoryViewer(EventHandler): baseEventHandler: EventHandler def __init__(self, engine: Engine, baseEventHandler: EventHandler): super().__init__(engine) self.baseEventHandler = baseEventHandler self.log_length = len(engine.message_log.messages) self.cursor = self.log_length - 1 def render(self, console: tcod.console.Console) -> None: super().render(console) log_console = tcod.console.Console(console.width - 6, console.height - 6) #rendering a nice log window log_console.draw_frame( 0,0, log_console.width, log_console.height, # "╔═╗║ ║╚═╝" decoration="\\x/x x/x\\", fg=colors.terminal_dark, bg=colors.black ) log_console.print_box( 0, 0, log_console.width, log_console.height, string = "Message History", alignment=tcod.constants.CENTER, fg=colors.terminal_light, bg=colors.black ) self.engine.message_log.render_messages( log_console, 2, 2, log_console.width - 4, log_console.height - 4, self.engine.message_log.messages[:self.cursor + 1] ) log_console.blit(console, 3, 3) #into the middle def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[BaseAction]: if event.sym in CURSOR_SCROLL_KEYS: adjust = CURSOR_SCROLL_KEYS[event.sym] if adjust < 0 and self.cursor == 0: pass #do nothing elif adjust > 0 and self.cursor == self.log_length - 1: pass #do nothing else: self.cursor = max(0, min(self.log_length - 1, self.cursor + adjust)) elif event.sym == tcod.event.KeySym.HOME: self.cursor = 0 elif event.sym == tcod.event.KeySym.END: self.cursor = self.log_length - 1 else: #return to the game self.engine.event_handler = self.baseEventHandler #generic tool class OptionSelector(EventHandler): baseEventHandler: EventHandler def __init__(self, engine: Engine, baseEventHandler: EventHandler, options: List[str], callback: function): super().__init__(engine) self.baseEventHandler = baseEventHandler self.options = options self.callback = callback self.length = len(options) self.cursor = 0 def render(self, console: tcod.console.Console) -> None: super().render(console) select_console = tcod.console.Console(console.width - 20, console.height - 16) #rendering a nice list window select_console.draw_frame( 0,0, select_console.width, select_console.height, # "╔═╗║ ║╚═╝" decoration="\\x/x x/x\\", fg=colors.terminal_dark, bg=colors.black ) select_console.print_box( 0, 0, select_console.width, select_console.height, string = "Select One", alignment=tcod.constants.CENTER, fg=colors.terminal_light, bg=colors.black ) #render the cursor & options offset = 0 for option in self.options: select_console.print( 4, 2 + offset, string = option, fg=colors.terminal_light, bg=colors.black, ) offset += 1 select_console.print(2, 2 + self.cursor, string = ">", fg=colors.terminal_light, bg=colors.black) select_console.blit(console, 10, 8) def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[BaseAction]: if event.sym in CURSOR_SCROLL_KEYS: adjust = CURSOR_SCROLL_KEYS[event.sym] if adjust < 0 and self.cursor == 0: pass #do nothing elif adjust > 0 and self.cursor == self.length - 1: pass #do nothing else: self.cursor = max(0, min(self.length - 1, self.cursor + adjust)) elif event.sym in CURSOR_CONFIRM_KEYS: #got the answer self.callback(self.cursor) self.engine.event_handler = self.baseEventHandler elif event.sym == tcod.event.KeySym.HOME: self.cursor = 0 elif event.sym == tcod.event.KeySym.END: self.cursor = self.length - 1 else: #return to the game self.engine.event_handler = self.baseEventHandler