diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index 8f5bb5a..e0e707d 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -1,45 +1,94 @@ +import sys from typing import Any from .entities.player import Player from .interfaces import Map from .mapdisplay import MapDisplay from .settings import Settings -from .term_manager import TermManager +from enum import Enum, auto +from . import menus + + +class GameMode(Enum): + MAINMENU = auto() + PLAY = auto() + SETTINGS = auto() + INVENTORY = auto() + + +class KeyValues(Enum): + UP = auto() + DOWN = auto() + LEFT = auto() + RIGHT = auto() + ENTER = auto() class Game: - INSTANCE = None - - def init(self) -> None: - Game.INSTANCE = self + def __init__(self) -> None: + self.state = GameMode.MAINMENU + self.main_menu = menus.MainMenu() self.settings = Settings() self.settings.load_settings() self.settings.write_settings() - with TermManager() as term_manager: - self._start_game(term_manager.screen) - def _start_game(self, screen: Any) -> None: - # TODO Generate map, or make the possibility to load another one - m = Map.load("example_map.txt") - player = Player() - self.player = player - m.add_entity(player) - player.move(1, 6) - d = MapDisplay(m, player) + def new_game(self, init_pad: bool = True) -> None: + # TODO generate a new map procedurally + self.m = Map.load("example_map.txt") + self.player = Player() + self.player.move(1, 6) + self.m.add_entity(self.player) + self.d = MapDisplay(self.m, self.player, init_pad) + + @staticmethod + def load_game(filename: str) -> None: + # TODO loading map from a file + raise NotImplementedError() + + def run(self, screen: Any) -> None: while True: screen.clear() screen.refresh() - d.display(player.y, player.y) + self.d.display(self.player.y, self.player.x) key = screen.getkey() - self.handle_key_pressed(key) + self.handle_key_pressed(self.translate_key(key)) - def handle_key_pressed(self, key: str) -> None: - # TODO load keys from settings - if key == 'z' or key == 'KEY_UP': - self.player.move_up() - if key == 's' or key == 'KEY_DOWN': - self.player.move_down() - if key == 'q' or key == 'KEY_LEFT': - self.player.move_left() - if key == 'd' or key == 'KEY_RIGHT': - self.player.move_right() + def translate_key(self, key: str) -> KeyValues: + if key in (self.settings.KEY_DOWN_SECONDARY, + self.settings.KEY_DOWN_PRIMARY): + return KeyValues.DOWN + elif key in (self.settings.KEY_LEFT_PRIMARY, + self.settings.KEY_LEFT_SECONDARY): + return KeyValues.LEFT + elif key in (self.settings.KEY_RIGHT_PRIMARY, + self.settings.KEY_RIGHT_SECONDARY): + return KeyValues.RIGHT + elif key in (self.settings.KEY_UP_PRIMARY, + self.settings.KEY_UP_SECONDARY): + return KeyValues.UP + elif key == self.settings.KEY_ENTER: + return KeyValues.ENTER + + def handle_key_pressed(self, key: KeyValues) -> None: + if self.state == GameMode.PLAY: + if key == KeyValues.UP: + self.player.move_up() + if key == KeyValues.DOWN: + self.player.move_down() + if key == KeyValues.LEFT: + self.player.move_left() + if key == KeyValues.RIGHT: + self.player.move_right() + if self.state == GameMode.MAINMENU: + if key == KeyValues.DOWN: + self.main_menu.go_down() + if key == KeyValues.UP: + self.main_menu.go_up() + if key == KeyValues.ENTER: + option = self.main_menu.validate() + if option == menus.MainMenuValues.START: + self.state = GameMode.PLAY + elif option == menus.MainMenuValues.SETTINGS: + self.state = GameMode.SETTINGS + elif option == menus.MainMenuValues.EXIT: + sys.exit(0) diff --git a/dungeonbattle/mapdisplay.py b/dungeonbattle/mapdisplay.py index fdc87ba..095b7cb 100644 --- a/dungeonbattle/mapdisplay.py +++ b/dungeonbattle/mapdisplay.py @@ -6,10 +6,11 @@ from dungeonbattle.interfaces import Map class MapDisplay: - def __init__(self, m: Map, player: Player): + def __init__(self, m: Map, player: Player, init_pad: bool = True): self.map = m - self.pad = curses.newpad(m.height, m.width + 1) self.player = player + if init_pad: + self.pad = curses.newpad(m.height, m.width + 1) def update_pad(self) -> None: self.pad.addstr(0, 0, self.map.draw_string()) diff --git a/dungeonbattle/menus.py b/dungeonbattle/menus.py new file mode 100644 index 0000000..82d6d4e --- /dev/null +++ b/dungeonbattle/menus.py @@ -0,0 +1,34 @@ +from enum import Enum, auto +from typing import Any + + +class Menu: + values: list + + def __init__(self): + self.position = 0 + + def go_up(self) -> None: + self.position = max(0, self.position - 1) + + def go_down(self) -> None: + self.position = min(len(self.values) - 1, self.position + 1) + + def validate(self) -> Any: + return self.values[self.position] + + +class MainMenuValues(Enum): + START = auto() + SETTINGS = auto() + EXIT = auto() + + +class MainMenu(Menu): + values = [e for e in MainMenuValues] + + +class ArbitraryMenu(Menu): + def __init__(self, values: list): + super().__init__() + self.values = values diff --git a/dungeonbattle/settings.py b/dungeonbattle/settings.py index 5c80b7c..db216df 100644 --- a/dungeonbattle/settings.py +++ b/dungeonbattle/settings.py @@ -27,6 +27,8 @@ class Settings: ['d', 'Touche principale pour aller vers la droite'] self.KEY_RIGHT_SECONDARY = \ ['KEY_RIGHT', 'Touche secondaire pour aller vers la droite'] + self.KEY_ENTER = \ + ['\n', 'Touche pour valider un menu'] self.TEXTURE_PACK = ['ASCII', 'Pack de textures utilisé'] def __getattribute__(self, item: str) -> Any: diff --git a/dungeonbattle/tests/game_test.py b/dungeonbattle/tests/game_test.py new file mode 100644 index 0000000..4183cbe --- /dev/null +++ b/dungeonbattle/tests/game_test.py @@ -0,0 +1,97 @@ +import unittest + +from dungeonbattle.game import Game, KeyValues, GameMode +from dungeonbattle.menus import MainMenuValues + + +class TestGame(unittest.TestCase): + def setUp(self) -> None: + """ + Setup game. + """ + self.game = Game() + self.game.new_game(False) + + def test_load_game(self) -> None: + self.assertRaises(NotImplementedError, Game.load_game, "game.save") + + def test_key_translation(self) -> None: + """ + Test key bindings. + """ + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_UP_PRIMARY), KeyValues.UP) + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_UP_SECONDARY), KeyValues.UP) + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_DOWN_PRIMARY), KeyValues.DOWN) + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_DOWN_SECONDARY), KeyValues.DOWN) + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_LEFT_PRIMARY), KeyValues.LEFT) + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_LEFT_SECONDARY), KeyValues.LEFT) + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_RIGHT_PRIMARY), KeyValues.RIGHT) + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_RIGHT_SECONDARY), KeyValues.RIGHT) + self.assertEqual(self.game.translate_key( + self.game.settings.KEY_ENTER), KeyValues.ENTER) + + def test_key_press(self) -> None: + """ + Press a key and see what is done. + """ + self.assertEqual(self.game.state, GameMode.MAINMENU) + self.assertEqual(self.game.main_menu.validate(), + MainMenuValues.START) + self.game.handle_key_pressed(KeyValues.UP) + self.assertEqual(self.game.main_menu.validate(), + MainMenuValues.START) + self.game.handle_key_pressed(KeyValues.DOWN) + self.assertEqual(self.game.main_menu.validate(), + MainMenuValues.SETTINGS) + self.game.handle_key_pressed(KeyValues.ENTER) + self.assertEqual(self.game.state, GameMode.SETTINGS) + + self.game.state = GameMode.MAINMENU + + self.game.handle_key_pressed(KeyValues.DOWN) + self.assertEqual(self.game.main_menu.validate(), + MainMenuValues.EXIT) + self.assertRaises(SystemExit, self.game.handle_key_pressed, + KeyValues.ENTER) + + self.game.handle_key_pressed(KeyValues.UP) + self.assertEqual(self.game.main_menu.validate(), + MainMenuValues.SETTINGS) + self.game.handle_key_pressed(KeyValues.UP) + self.assertEqual(self.game.main_menu.validate(), + MainMenuValues.START) + + self.game.handle_key_pressed(KeyValues.ENTER) + self.assertEqual(self.game.state, GameMode.PLAY) + + y, x = self.game.player.y, self.game.player.x + self.game.handle_key_pressed(KeyValues.DOWN) + new_y, new_x = self.game.player.y, self.game.player.x + self.assertEqual(new_y, y + 1) + self.assertEqual(new_x, x) + + y, x = new_y, new_x + self.game.handle_key_pressed(KeyValues.RIGHT) + new_y, new_x = self.game.player.y, self.game.player.x + self.assertEqual(new_y, y) + self.assertEqual(new_x, x + 1) + + y, x = self.game.player.y, self.game.player.x + self.game.handle_key_pressed(KeyValues.UP) + new_y, new_x = self.game.player.y, self.game.player.x + self.assertEqual(new_y, y - 1) + self.assertEqual(new_x, x) + + y, x = self.game.player.y, self.game.player.x + self.game.handle_key_pressed(KeyValues.LEFT) + new_y, new_x = self.game.player.y, self.game.player.x + self.assertEqual(new_y, y) + self.assertEqual(new_x, x - 1) diff --git a/dungeonbattle/tests/menus_test.py b/dungeonbattle/tests/menus_test.py new file mode 100644 index 0000000..6ad9df7 --- /dev/null +++ b/dungeonbattle/tests/menus_test.py @@ -0,0 +1,24 @@ +import unittest + +from dungeonbattle.menus import ArbitraryMenu, MainMenu, MainMenuValues + + +class TestMenus(unittest.TestCase): + def test_scroll_menu(self) -> None: + """ + Test to scroll the menu. + """ + arbitrary_menu = ArbitraryMenu([]) + self.assertEqual(arbitrary_menu.position, 0) + + main_menu = MainMenu() + self.assertEqual(main_menu.position, 0) + self.assertEqual(main_menu.validate(), MainMenuValues.START) + main_menu.go_up() + self.assertEqual(main_menu.validate(), MainMenuValues.START) + main_menu.go_down() + self.assertEqual(main_menu.validate(), MainMenuValues.SETTINGS) + main_menu.go_down() + self.assertEqual(main_menu.validate(), MainMenuValues.EXIT) + main_menu.go_down() + self.assertEqual(main_menu.validate(), MainMenuValues.EXIT) diff --git a/dungeonbattle/texturepack.py b/dungeonbattle/texturepack.py new file mode 100644 index 0000000..d9b5818 --- /dev/null +++ b/dungeonbattle/texturepack.py @@ -0,0 +1,8 @@ +# This is the base ascii texturepack + +ascii = { + "EMPTY": ' ', + "WALL": '#', + "FLOOR": '.', + "PLAYER": '@' +} diff --git a/main.py b/main.py index a3c6f79..2e44d9d 100755 --- a/main.py +++ b/main.py @@ -1,5 +1,9 @@ #!/usr/bin/env python from dungeonbattle.game import Game +from dungeonbattle.term_manager import TermManager if __name__ == "__main__": - Game().init() + with TermManager() as term_manager: + game = Game() + game.new_game() + game.run(term_manager.screen)