commit d4b977abf660a2821e73a2e7c4d4795b4ee7ad87 Author: Suguivy Date: Wed Mar 1 20:47:24 2023 +0100 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..00e71b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +log.txt +ideas.md +__pycache__/ \ No newline at end of file diff --git a/actions.py b/actions.py new file mode 100644 index 0000000..435657b --- /dev/null +++ b/actions.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass + +@dataclass +class Move: + x: int + y: int + +@dataclass +class Idle: + pass + +Action = Move | Idle \ No newline at end of file diff --git a/entity.py b/entity.py new file mode 100644 index 0000000..c9a12f2 --- /dev/null +++ b/entity.py @@ -0,0 +1,20 @@ +from enum import Enum +from actions import Action, Idle + +class Entity(): + pass + +class Item(Entity): + pass + +class Character(Entity): + def __init__(self): + self.action: Action = Idle() + +class Player(Character): + def __init__(self): + super().__init__() + +class Enemy(Character): + def __init__(self): + super().__init__() \ No newline at end of file diff --git a/floor.py b/floor.py new file mode 100644 index 0000000..e42c75c --- /dev/null +++ b/floor.py @@ -0,0 +1,38 @@ +from enum import Enum +from entity import Character, Entity, Item + +class TileType(Enum): + WALL = 1 + AIR = 2 + +class Floor: + def __init__(self, width, height): + self.width = width + self.height = height + self.grid = [[TileType.AIR] * height] * width + self.entities = { + Character: {}, + Item: {} + } + + def get_tile(self, x, y): + return self.grid[x][y] + + def add_entity(self, x, y, entity: Entity) -> bool: + if isinstance(entity, Character): + if (x, y) in self.entities[Character]: + return False + else: + self.entities[Character][(x, y)] = entity + return True + elif isinstance(entity, Item): + if (x, y) not in self.entities[Item]: + self.entities[Item][(x,y)] = [] + self.entities[Item][(x,y)].append(entity) + return True + else: + return False + + def pop_character(self, x, y) -> Character | None: + if (x, y) in self.entities[Character]: + return self.entities[Character].pop((x, y)) \ No newline at end of file diff --git a/game.py b/game.py new file mode 100644 index 0000000..9d08472 --- /dev/null +++ b/game.py @@ -0,0 +1,65 @@ +from terminal import Terminal +from entity import Character, Player, Item +from actions import * +from floor import Floor, TileType + +TEXTURES = { + TileType.WALL: '#', + TileType.AIR: '.', + Player: '@' +} + +class Game: + def __init__(self): + self.term = Terminal() + self.player = Player() + self.floor = Floor(20, 10) + self.should_exit = False + + def run(self): + """ + Runs the game + """ + self.floor.add_entity(3, 3, self.player) + while not self.should_exit: + self.render() + self.get_event() + self.step() + + def render(self): + """ + Renders the game + """ + for x in range(self.floor.width): + for y in range(self.floor.height): + if (x, y) in self.floor.entities[Character]: + self.term.put_char(x, y, TEXTURES[Player]) + elif (x, y) in self.floor.entities[Item]: + self.term.put_char(x, y, 'i') + else: + self.term.put_char(x, y, TEXTURES[self.floor.get_tile(x, y)]) + + def step(self): + """ + Performs a turn in the game + """ + for (pos, character) in dict(self.floor.entities[Character]).items(): + if isinstance(character.action, Move): + character = self.floor.entities[Character].pop(pos) + self.floor.add_entity(pos[0] + character.action.x, pos[1] + character.action.y, character) + + def get_event(self): + """ + Waits for a game event to happen + """ + keycode = self.term.get_key() + if keycode == "q": + self.should_exit = True + elif keycode == "KEY_RIGHT": + self.player.action = Move(1, 0) + elif keycode == "KEY_LEFT": + self.player.action = Move(-1, 0) + elif keycode == "KEY_DOWN": + self.player.action = Move(0, 1) + elif keycode == "KEY_UP": + self.player.action = Move(0, -1) \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..02688b7 --- /dev/null +++ b/main.py @@ -0,0 +1,8 @@ +from game import Game + +def main(): + game = Game() + game.run() + +if __name__ == "__main__": + main() diff --git a/terminal.py b/terminal.py new file mode 100644 index 0000000..b1d3614 --- /dev/null +++ b/terminal.py @@ -0,0 +1,44 @@ +import curses + +KEYCODES = { + curses.KEY_LEFT: "KEY_LEFT", + curses.KEY_RIGHT: "KEY_RIGHT", + curses.KEY_UP: "KEY_UP", + curses.KEY_DOWN: "KEY_DOWN" +} + +class Terminal: + def __init__(self): + """ + Perfoms some initial steps to set the terminal ready for the game + """ + self.scr = curses.initscr() + curses.noecho() + curses.cbreak() + curses.curs_set(0) + self.scr.keypad(True) + + def __del__(self): + """ + Perfoms some cleanup to restore the behaviour of the terminal + """ + curses.nocbreak() + self.scr.keypad(False) + curses.echo() + curses.endwin() + + def get_key(self) -> str: + """ + Blocks until a key is read + """ + ch = self.scr.getch() + if ch in KEYCODES: + return KEYCODES[ch] + else: + return chr(ch) + + def put_char(self, x, y, char): + """ + Prints a char at the specified position + """ + self.scr.addch(y, x, char) \ No newline at end of file