Last active
January 16, 2020 16:19
-
-
Save seansawyer/f41c95b16bc1dae5b2254abfadbee6b9 to your computer and use it in GitHub Desktop.
Boilerplate for a roguelike implemented as an FSM using libtcod
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from dataclasses import dataclass | |
from enum import Enum | |
from typing import Callable, Dict, Optional, Tuple | |
import tcod | |
import tcod.event | |
@dataclass | |
class Game: | |
# drawing context | |
root_console: tcod.console.Console | |
draw_console: tcod.console.Console | |
# console settings | |
width: int = 80 | |
height: int = 50 | |
# player state | |
player_x: int = 40 | |
player_y: int = 25 | |
class State(Enum): | |
MAP = 'map' | |
ENDGAME = 'endgame' | |
class StateHandler(tcod.event.EventDispatch): | |
def __init__(self, next_state: Optional[State], game: Game) -> None: | |
self.next_state = next_state | |
self.game = game | |
def handle(self) -> Tuple[Optional[State], Game]: | |
self.draw() | |
for event in tcod.event.wait(): | |
self.dispatch(event) | |
return self.next_state, self.game | |
def draw(self) -> None: | |
pass | |
class MapStateHandler(StateHandler): | |
def draw(self): | |
self.game.draw_console.clear() | |
self.game.draw_console.put_char( | |
self.game.player_x, | |
self.game.player_y, | |
ord('@') | |
) | |
self.game.draw_console.blit( | |
self.game.root_console, | |
width=self.game.draw_console.width, | |
height=self.game.draw_console.height | |
) | |
tcod.console_flush() | |
def ev_quit(self, event): | |
self.next_state = None | |
def ev_keydown(self, event): | |
print(event) | |
if event.scancode == tcod.event.SCANCODE_F: | |
fullscreen = not tcod.console_is_fullscreen() | |
tcod.console_set_fullscreen(fullscreen) | |
elif event.scancode == tcod.event.SCANCODE_Q: | |
self.next_state = None | |
elif event.scancode == tcod.event.SCANCODE_W: | |
self.next_state = State.ENDGAME | |
elif event.scancode == tcod.event.SCANCODE_H: | |
self.game.player_x -= 1 # left | |
elif event.scancode == tcod.event.SCANCODE_J: | |
self.game.player_y += 1 # up | |
elif event.scancode == tcod.event.SCANCODE_K: | |
self.game.player_y -= 1 # down | |
elif event.scancode == tcod.event.SCANCODE_L: | |
self.game.player_x += 1 # right | |
class EndgameStateHandler(StateHandler): | |
def draw(self): | |
self.game.draw_console.clear() | |
self.game.draw_console.print(1, 1, f'You win!') | |
self.game.draw_console.print(1, 3, 'Press R to play again') | |
self.game.draw_console.print(1, 5, 'Press Q to quit') | |
self.game.draw_console.blit( | |
self.game.root_console, | |
width=self.game.draw_console.width, | |
height=self.game.draw_console.height | |
) | |
tcod.console_flush() | |
def ev_quit(self, event): | |
self.next_state = None | |
def ev_keydown(self, event): | |
print(event) | |
if event.scancode == tcod.event.SCANCODE_F: | |
fullscreen = not tcod.console_is_fullscreen() | |
tcod.console_set_fullscreen(fullscreen) | |
elif event.scancode == tcod.event.SCANCODE_Q: | |
self.next_state = None | |
elif event.scancode == tcod.event.SCANCODE_R: | |
self.next_state = State.MAP | |
def run_fsm( | |
state_handlers: Dict[State, StateHandler], | |
state: State, | |
game: Game | |
) -> None: | |
while state is not None: | |
handler_class = state_handlers[state] | |
handler = handler_class(state, game) | |
state, game = handler.handle() | |
def main(): | |
tcod.console_set_custom_font( | |
"arial10x10.png", | |
tcod.FONT_LAYOUT_TCOD | tcod.FONT_TYPE_GREYSCALE, | |
) | |
with tcod.console_init_root( | |
80, | |
50, | |
order='F', | |
renderer=tcod.RENDERER_SDL2, | |
title='FSM Game', | |
vsync=True | |
) as root_console: | |
draw_console = tcod.console.Console(80, 50, order='F') | |
game = Game(root_console=root_console, draw_console=draw_console) | |
my_state_handlers = { | |
State.MAP: MapStateHandler, | |
State.ENDGAME: EndgameStateHandler, | |
} | |
run_fsm(my_state_handlers, State.MAP, game) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment