Skip to content

Instantly share code, notes, and snippets.

@seansawyer
Last active January 16, 2020 16:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seansawyer/f41c95b16bc1dae5b2254abfadbee6b9 to your computer and use it in GitHub Desktop.
Save seansawyer/f41c95b16bc1dae5b2254abfadbee6b9 to your computer and use it in GitHub Desktop.
Boilerplate for a roguelike implemented as an FSM using libtcod
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