2022-01-07 16:25:07 -05:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2022-01-10 14:22:33 -05:00
|
|
|
from typing import overload, Optional, TYPE_CHECKING
|
2022-01-06 11:09:43 -05:00
|
|
|
|
|
|
|
import tcod.event
|
|
|
|
|
2022-01-10 13:47:10 -05:00
|
|
|
from actions import Action, BumpAction, EscapeAction, WaitAction
|
2022-01-06 11:09:43 -05:00
|
|
|
|
2022-01-07 16:25:07 -05:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from engine import Engine
|
|
|
|
|
2022-01-10 13:47:10 -05:00
|
|
|
MOVE_KEYS = {
|
|
|
|
# Arrow keys
|
|
|
|
tcod.event.K_UP: (0, -1),
|
|
|
|
tcod.event.K_DOWN: (0, 1),
|
|
|
|
tcod.event.K_LEFT: (-1, 0),
|
|
|
|
tcod.event.K_RIGHT: (1, 0),
|
|
|
|
tcod.event.K_HOME: (-1, -1),
|
|
|
|
tcod.event.K_END: (-1, 1),
|
|
|
|
tcod.event.K_PAGEUP: (1, -1),
|
|
|
|
tcod.event.K_PAGEDOWN: (1, 1),
|
|
|
|
|
|
|
|
# Numpad keys
|
|
|
|
tcod.event.K_KP_1: (-1, 1),
|
|
|
|
tcod.event.K_KP_2: (0, 1),
|
|
|
|
tcod.event.K_KP_3: (1, 1),
|
|
|
|
tcod.event.K_KP_4: (-1, 0),
|
|
|
|
tcod.event.K_KP_6: (1, 0),
|
|
|
|
tcod.event.K_KP_7: (-1, -1),
|
|
|
|
tcod.event.K_KP_8: (0, -1),
|
|
|
|
tcod.event.K_KP_9: (1, -1),
|
|
|
|
|
|
|
|
# Vi keys
|
|
|
|
tcod.event.K_h: (-1, 0),
|
|
|
|
tcod.event.K_j: (0, 1),
|
|
|
|
tcod.event.K_k: (0, -1),
|
|
|
|
tcod.event.K_l: (1, 0),
|
|
|
|
tcod.event.K_y: (-1, -1),
|
|
|
|
tcod.event.K_u: (1, -1),
|
|
|
|
tcod.event.K_b: (-1, 1),
|
|
|
|
tcod.event.K_n: (1, 1),
|
|
|
|
}
|
|
|
|
|
|
|
|
WAIT_KEYS = {
|
|
|
|
tcod.event.K_PERIOD,
|
|
|
|
tcod.event.K_KP_5,
|
|
|
|
tcod.event.K_CLEAR,
|
|
|
|
}
|
|
|
|
|
2022-01-06 11:09:43 -05:00
|
|
|
|
|
|
|
class EventHandler(tcod.event.EventDispatch[Action]):
|
2022-01-07 16:25:07 -05:00
|
|
|
def __init__(self, engine: Engine):
|
|
|
|
self.engine = engine
|
|
|
|
|
2022-01-11 16:21:42 -05:00
|
|
|
def handle_events(self, context: tcod.context.Context) -> None:
|
|
|
|
for event in tcod.event.wait():
|
|
|
|
context.convert_event(event)
|
|
|
|
self.dispatch(event)
|
|
|
|
|
|
|
|
def ev_mousemotion(self, event: tcod.event.MouseMotion) -> None:
|
|
|
|
if self.engine.game_map.in_bounds(event.tile.x, event.tile.y):
|
|
|
|
self.engine.mouse_location = event.tile.x, event.tile.y
|
2022-01-10 14:22:33 -05:00
|
|
|
|
|
|
|
def ev_quit(self, event: tcod.event.Quit) -> Optional[Action]:
|
|
|
|
raise SystemExit()
|
|
|
|
|
2022-01-11 16:21:42 -05:00
|
|
|
def on_render(self, console: tcod.Console) -> None:
|
|
|
|
self.engine.render(console)
|
|
|
|
|
2022-01-10 14:22:33 -05:00
|
|
|
|
|
|
|
class MainGameEventHandler(EventHandler):
|
2022-01-11 16:21:42 -05:00
|
|
|
def handle_events(self, context: tcod.context.Context) -> None:
|
2022-01-07 16:25:07 -05:00
|
|
|
for event in tcod.event.wait():
|
2022-01-11 16:21:42 -05:00
|
|
|
context.convert_event(event)
|
|
|
|
|
2022-01-07 16:25:07 -05:00
|
|
|
action = self.dispatch(event)
|
|
|
|
|
|
|
|
if action is None:
|
|
|
|
continue
|
|
|
|
|
|
|
|
action.perform()
|
|
|
|
|
|
|
|
self.engine.handle_enemy_turns()
|
|
|
|
self.engine.update_fov()
|
|
|
|
|
2022-01-06 11:09:43 -05:00
|
|
|
def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[Action]:
|
|
|
|
action: Optional[Action] = None
|
|
|
|
|
|
|
|
key = event.sym
|
|
|
|
|
2022-01-07 16:25:07 -05:00
|
|
|
player = self.engine.player
|
|
|
|
|
2022-01-10 13:47:10 -05:00
|
|
|
if key in MOVE_KEYS:
|
|
|
|
dx, dy = MOVE_KEYS[key]
|
|
|
|
action = BumpAction(player, dx, dy)
|
|
|
|
elif key in WAIT_KEYS:
|
|
|
|
action = WaitAction(player)
|
2022-01-06 11:09:43 -05:00
|
|
|
|
|
|
|
elif key == tcod.event.K_ESCAPE:
|
2022-01-07 16:25:07 -05:00
|
|
|
action = EscapeAction(player)
|
2022-01-11 16:33:18 -05:00
|
|
|
elif key == tcod.event.K_v:
|
|
|
|
self.engine.event_handler = HistoryViewer(self.engine)
|
2022-01-06 11:09:43 -05:00
|
|
|
|
|
|
|
# No valid key was pressed
|
|
|
|
return action
|
2022-01-10 14:22:33 -05:00
|
|
|
|
|
|
|
|
|
|
|
class GameOverEventHandler(EventHandler):
|
2022-01-11 16:21:42 -05:00
|
|
|
def handle_events(self, context: tcod.context.Context) -> None:
|
2022-01-10 14:22:33 -05:00
|
|
|
for event in tcod.event.wait():
|
|
|
|
action = self.dispatch(event)
|
|
|
|
|
|
|
|
if action is None:
|
|
|
|
continue
|
|
|
|
|
|
|
|
action.perform()
|
|
|
|
|
|
|
|
def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[Action]:
|
|
|
|
action: Optional[Action] = None
|
|
|
|
|
|
|
|
key = event.sym
|
|
|
|
|
|
|
|
if key == tcod.event.K_ESCAPE:
|
|
|
|
action = EscapeAction(self.engine.player)
|
|
|
|
|
|
|
|
# No valid key was pressed
|
|
|
|
return action
|
2022-01-11 16:33:18 -05:00
|
|
|
|
|
|
|
|
|
|
|
CURSOR_Y_KEYS = {
|
|
|
|
tcod.event.K_UP: -1,
|
|
|
|
tcod.event.K_DOWN: 1,
|
|
|
|
tcod.event.K_PAGEUP: -10,
|
|
|
|
tcod.event.K_PAGEDOWN: 10,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class HistoryViewer(EventHandler):
|
|
|
|
"""Print the history on a larger window which can be navigated."""
|
|
|
|
|
|
|
|
def __init__(self, engine: Engine):
|
|
|
|
super().__init__(engine)
|
|
|
|
self.log_length = len(engine.message_log.messages)
|
|
|
|
self.cursor = self.log_length - 1
|
|
|
|
|
|
|
|
def on_render(self, console: tcod.Console) -> None:
|
|
|
|
super().on_render(console) # Draw the main state as the background.
|
|
|
|
|
|
|
|
log_console = tcod.Console(console.width - 6, console.height - 6)
|
|
|
|
|
|
|
|
# Draw a frame with a custom banner title.
|
|
|
|
log_console.draw_frame(0, 0, log_console.width, log_console.height)
|
|
|
|
log_console.print_box(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
log_console.width,
|
|
|
|
1,
|
|
|
|
"┤Message history├",
|
|
|
|
alignment=tcod.CENTER
|
|
|
|
)
|
|
|
|
|
|
|
|
# Render the message log using the cursor parameter.
|
|
|
|
self.engine.message_log.render_messages(
|
|
|
|
log_console,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
log_console.width - 2,
|
|
|
|
log_console.height - 2,
|
|
|
|
self.engine.message_log.messages[: self.cursor + 1],
|
|
|
|
)
|
|
|
|
log_console.blit(console, 3, 3)
|
|
|
|
|
|
|
|
def ev_keydown(self, event: tcod.event.KeyDown) -> None:
|
|
|
|
# Fancy conditional movement to make it feel right.
|
|
|
|
if event.sym in CURSOR_Y_KEYS:
|
|
|
|
adjust = CURSOR_Y_KEYS[event.sym]
|
|
|
|
if adjust < 0 and self.cursor == 0:
|
|
|
|
# Only move from the top to the bottom when you're on the edge.
|
|
|
|
self.cursor = self.log_length - 1
|
|
|
|
elif adjust > 0 and self.cursor == self.log_length - 1:
|
|
|
|
# Same with bottom to top movement.
|
|
|
|
self.cursor = 0
|
|
|
|
else:
|
|
|
|
# Otherwise move while staying clamped to the bounds of the history log.
|
|
|
|
self.cursor = max(0, min(self.cursor + adjust, self.log_length - 1))
|
|
|
|
elif event.sym == tcod.event.K_HOME:
|
|
|
|
self.cursor = 0 # Move directly to the top message.
|
|
|
|
elif event.sym == tcod.event.K_END:
|
|
|
|
self.cursor = self.log_length - 1 # Move directly to the last message.
|
|
|
|
else: # Any other key moves back to the main game state.
|
|
|
|
self.engine.event_handler = MainGameEventHandler(self.engine)
|