Stepwise/source/event_handlers.py
Kayne Ruse f831019148 Items can be picked up and stored in the inventory
When more than one item can be picked up, and options window is shown.

Stubs for "using" an item are in place.
2025-03-29 16:30:44 +11:00

278 lines
7.4 KiB
Python

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