From 63660707d1134d6efa7ce0b7780e2d759112fc9d Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 16 Oct 2020 17:58:00 +0200 Subject: [PATCH 01/23] A basic fighting mechanic and a few entities --- dungeonbattle/entities/__init__.py | 0 dungeonbattle/entities/monsters.py | 5 +++++ dungeonbattle/entities/player.py | 5 +++++ dungeonbattle/interfaces.py | 19 +++++++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 dungeonbattle/entities/__init__.py create mode 100644 dungeonbattle/entities/monsters.py create mode 100644 dungeonbattle/entities/player.py diff --git a/dungeonbattle/entities/__init__.py b/dungeonbattle/entities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dungeonbattle/entities/monsters.py b/dungeonbattle/entities/monsters.py new file mode 100644 index 0000000..c9cdf1c --- /dev/null +++ b/dungeonbattle/entities/monsters.py @@ -0,0 +1,5 @@ +from ..interfaces import FightingEntity + +class Squirrel(FightingEntity): + maxhealth = 10 + strength = 3 diff --git a/dungeonbattle/entities/player.py b/dungeonbattle/entities/player.py new file mode 100644 index 0000000..66aed0c --- /dev/null +++ b/dungeonbattle/entities/player.py @@ -0,0 +1,5 @@ +from ..interfaces import FightingEntity + +class Player(FightingEntity): + maxhealth = 20 + strength = 5 \ No newline at end of file diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 838cbec..3ba9efd 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -50,3 +50,22 @@ class Entity: def move(self, x: int, y: int) -> None: self.tile.x = x self.tile.y = y + +class FightingEntity(Entity): + maxhealth: int + health: int + strength: int + + def __init__(self): + self.health = self.maxhealth + + def hit(self, opponent) -> None: + opponent.take_damage(self, self.strength) + + def take_damage(self, attacker, amount:int) -> None: + self.health -= amount + if self.health <= 0: + self.die() + + def die(self) -> None: + pass From e65a486b783f70b08c76787fa6ab62c8882dd78b Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 16 Oct 2020 17:58:00 +0200 Subject: [PATCH 02/23] A basic fighting mechanic and a few entities --- dungeonbattle/entities/__init__.py | 0 dungeonbattle/entities/monsters.py | 5 +++++ dungeonbattle/entities/player.py | 5 +++++ dungeonbattle/interfaces.py | 19 +++++++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 dungeonbattle/entities/__init__.py create mode 100644 dungeonbattle/entities/monsters.py create mode 100644 dungeonbattle/entities/player.py diff --git a/dungeonbattle/entities/__init__.py b/dungeonbattle/entities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dungeonbattle/entities/monsters.py b/dungeonbattle/entities/monsters.py new file mode 100644 index 0000000..c9cdf1c --- /dev/null +++ b/dungeonbattle/entities/monsters.py @@ -0,0 +1,5 @@ +from ..interfaces import FightingEntity + +class Squirrel(FightingEntity): + maxhealth = 10 + strength = 3 diff --git a/dungeonbattle/entities/player.py b/dungeonbattle/entities/player.py new file mode 100644 index 0000000..66aed0c --- /dev/null +++ b/dungeonbattle/entities/player.py @@ -0,0 +1,5 @@ +from ..interfaces import FightingEntity + +class Player(FightingEntity): + maxhealth = 20 + strength = 5 \ No newline at end of file diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 7824134..a87825f 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -40,3 +40,22 @@ class Entity: def move(self, x: int, y: int) -> None: self.x = x self.y = y + +class FightingEntity(Entity): + maxhealth: int + health: int + strength: int + + def __init__(self): + self.health = self.maxhealth + + def hit(self, opponent) -> None: + opponent.take_damage(self, self.strength) + + def take_damage(self, attacker, amount:int) -> None: + self.health -= amount + if self.health <= 0: + self.die() + + def die(self) -> None: + pass From 11a7ce98259a9530294a4758dc1044757de7423a Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 16 Oct 2020 14:12:39 +0200 Subject: [PATCH 03/23] Added a first try for player movement --- dungeonbattle/player_move_essai2.py | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 dungeonbattle/player_move_essai2.py diff --git a/dungeonbattle/player_move_essai2.py b/dungeonbattle/player_move_essai2.py new file mode 100644 index 0000000..bf56764 --- /dev/null +++ b/dungeonbattle/player_move_essai2.py @@ -0,0 +1,45 @@ +import curses +#import term_manager.py as term +import time + +filename = "map.txt" +A = open(filename) +M = [] + +i = 0 +for lines in A : + B = list(lines)[:-1] + M.append(B) + i+=1 +print(M) + +def main(stdscr): + for y in range(len(M)): #len(M) + for x in range(len(M[0])): #len(M[0]) + stdscr.addstr(y,x,M[y][x]) + stdscr.refresh() + + cur = [1,6] #(y,x) + stdscr.addstr(1,6,'@') + stdscr.refresh() + + for i in range(10) : + stdscr.addstr(cur[0],cur[1],'.') + key = stdscr.getkey() + if key == 'z' : + if cur[0]>0 and M[cur[0]-1][cur[1]] != '#' : + cur[0] = cur[0]-1 + if key == 's' : + if cur[0]0 and M[cur[0]][cur[1]-1] != '#' : + cur[1] = cur[1]-1 + if key == 'd' : + if cur[1] Date: Fri, 16 Oct 2020 14:18:33 +0200 Subject: [PATCH 04/23] Added a map example --- dungeonbattle/map.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 dungeonbattle/map.txt diff --git a/dungeonbattle/map.txt b/dungeonbattle/map.txt new file mode 100644 index 0000000..4111fae --- /dev/null +++ b/dungeonbattle/map.txt @@ -0,0 +1,17 @@ + ####### ############# + #.....# #...........# + #.....# #####...........# + #.....# #...............# + #.##### #.###...........# + #.# #.# #...........# + #.# #.# ############# + #.# #.# + #.#### #.# + #....# #.# + ####.###################.# + #.....................# ################# + #.....................# #...............# + #.....................#######...............# + #...........................................# + #.....................#######...............# + ####################### ################# From e448697e6b2521c322daf91abbfaf19383ed7b3b Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 14:23:11 +0200 Subject: [PATCH 05/23] Replace the dot cursor after the player moved, not before --- dungeonbattle/player_move_essai2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dungeonbattle/player_move_essai2.py b/dungeonbattle/player_move_essai2.py index bf56764..6d4d73a 100644 --- a/dungeonbattle/player_move_essai2.py +++ b/dungeonbattle/player_move_essai2.py @@ -24,8 +24,8 @@ def main(stdscr): stdscr.refresh() for i in range(10) : - stdscr.addstr(cur[0],cur[1],'.') key = stdscr.getkey() + stdscr.addstr(cur[0],cur[1],'.') if key == 'z' : if cur[0]>0 and M[cur[0]-1][cur[1]] != '#' : cur[0] = cur[0]-1 From 4175238297c251badaa88db771bc4047a664b6e5 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 16 Oct 2020 15:00:33 +0200 Subject: [PATCH 06/23] Now uses TermManager --- dungeonbattle/player_move_essai2.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dungeonbattle/player_move_essai2.py b/dungeonbattle/player_move_essai2.py index 6d4d73a..ea44442 100644 --- a/dungeonbattle/player_move_essai2.py +++ b/dungeonbattle/player_move_essai2.py @@ -1,5 +1,5 @@ import curses -#import term_manager.py as term +from term_manager import TermManager import time filename = "map.txt" @@ -39,7 +39,6 @@ def main(stdscr): if cur[1] Date: Fri, 16 Oct 2020 15:23:58 +0200 Subject: [PATCH 07/23] Cleaner proof of concept --- dungeonbattle/player_move_essai2.py | 44 ------------------------ dungeonbattle/proof_of_concept.py | 40 +++++++++++++++++++++ dungeonbattle/map.txt => example_map.txt | 0 main.py | 3 +- 4 files changed, 42 insertions(+), 45 deletions(-) delete mode 100644 dungeonbattle/player_move_essai2.py create mode 100644 dungeonbattle/proof_of_concept.py rename dungeonbattle/map.txt => example_map.txt (100%) mode change 100644 => 100755 main.py diff --git a/dungeonbattle/player_move_essai2.py b/dungeonbattle/player_move_essai2.py deleted file mode 100644 index ea44442..0000000 --- a/dungeonbattle/player_move_essai2.py +++ /dev/null @@ -1,44 +0,0 @@ -import curses -from term_manager import TermManager -import time - -filename = "map.txt" -A = open(filename) -M = [] - -i = 0 -for lines in A : - B = list(lines)[:-1] - M.append(B) - i+=1 -print(M) - -def main(stdscr): - for y in range(len(M)): #len(M) - for x in range(len(M[0])): #len(M[0]) - stdscr.addstr(y,x,M[y][x]) - stdscr.refresh() - - cur = [1,6] #(y,x) - stdscr.addstr(1,6,'@') - stdscr.refresh() - - for i in range(10) : - key = stdscr.getkey() - stdscr.addstr(cur[0],cur[1],'.') - if key == 'z' : - if cur[0]>0 and M[cur[0]-1][cur[1]] != '#' : - cur[0] = cur[0]-1 - if key == 's' : - if cur[0]0 and M[cur[0]][cur[1]-1] != '#' : - cur[1] = cur[1]-1 - if key == 'd' : - if cur[1] None: + matrix = [] + with open("example_map.txt") as f: + for line in f.readlines(): + matrix.append(line[:-1]) + + with TermManager() as term_manager: + stdscr = term_manager.screen + + for y in range(len(matrix)): + for x in range(len(matrix[0])): + stdscr.addstr(y, x, matrix[y][x]) + stdscr.refresh() + + cur = [1, 6] # (y,x) + stdscr.addstr(1, 6, '@') + stdscr.refresh() + + while True: + key = stdscr.getkey() + stdscr.addstr(cur[0], cur[1], '.') + if key == 'z': + if cur[0] > 0 and matrix[cur[0] - 1][cur[1]] != '#': + cur[0] = cur[0] - 1 + if key == 's': + if cur[0] < len(matrix) - 1 and \ + matrix[cur[0] + 1][cur[1]] != '#': + cur[0] = cur[0] + 1 + if key == 'q': + if cur[1] > 0 and matrix[cur[0]][cur[1] - 1] != '#': + cur[1] = cur[1] - 1 + if key == 'd': + if cur[1] < len(matrix[0]) and \ + matrix[cur[0]][cur[1] + 1] != '#': + cur[1] = cur[1] + 1 + stdscr.addstr(cur[0], cur[1], '@') diff --git a/dungeonbattle/map.txt b/example_map.txt similarity index 100% rename from dungeonbattle/map.txt rename to example_map.txt diff --git a/main.py b/main.py old mode 100644 new mode 100755 index 1158de2..0eb26bf --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +from dungeonbattle.proof_of_concept import proof_of_concept if __name__ == "__main__": - print("Hello world!") + proof_of_concept() From d2f8a3b62331793bc5b71e5c67f8c5b23644c7d2 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 15:32:15 +0200 Subject: [PATCH 08/23] Also use arrow keys in example --- dungeonbattle/proof_of_concept.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dungeonbattle/proof_of_concept.py b/dungeonbattle/proof_of_concept.py index 8fe75c1..046d687 100644 --- a/dungeonbattle/proof_of_concept.py +++ b/dungeonbattle/proof_of_concept.py @@ -23,17 +23,17 @@ def proof_of_concept() -> None: while True: key = stdscr.getkey() stdscr.addstr(cur[0], cur[1], '.') - if key == 'z': + if key == 'z' or key == 'KEY_UP': if cur[0] > 0 and matrix[cur[0] - 1][cur[1]] != '#': cur[0] = cur[0] - 1 - if key == 's': + if key == 's' or key == 'KEY_DOWN': if cur[0] < len(matrix) - 1 and \ matrix[cur[0] + 1][cur[1]] != '#': cur[0] = cur[0] + 1 - if key == 'q': + if key == 'q' or key == 'KEY_LEFT': if cur[1] > 0 and matrix[cur[0]][cur[1] - 1] != '#': cur[1] = cur[1] - 1 - if key == 'd': + if key == 'd' or key == 'KEY_RIGHT': if cur[1] < len(matrix[0]) and \ matrix[cur[0]][cur[1] + 1] != '#': cur[1] = cur[1] + 1 From 182af96da0d76a4064c62bc1d317142655b8d1b9 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 16 Oct 2020 15:41:25 +0200 Subject: [PATCH 09/23] Tiles are now an enumeration --- dungeonbattle/interfaces.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index a87825f..507ebde 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +from enum import Enum, auto + class Map: @@ -24,18 +26,24 @@ class Map: lines = [line for line in lines if line] height = len(lines) width = len(lines[0]) - return Map(width, height, lines, []) + tiles = [[Tile.from_char(c) + for x, c in enumerate(line)] for y, line in enumerate(lines)] + return Map(width, height, tiles) + + +class Tile(Enum): + EMPTY = auto() + WALL = auto() + FLOOR = auto() + + @staticmethod + def from_char(c: str): + return {'#': Tile.WALL, '.': Tile.FLOOR, ' ': Tile.EMPTY}[c] class Entity: - y: int x: int - img: str - - def __init__(self, y: int, x: int, img: str): - self.y = y - self.x = x - self.img = img + y: int def move(self, x: int, y: int) -> None: self.x = x From eb1e125d4e6afd7349c07e920b52d795529fe7ce Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 14:00:38 +0200 Subject: [PATCH 10/23] Draw a map in a string to make the render in the screen easier --- dungeonbattle/interfaces.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 507ebde..ae4937c 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -30,6 +30,9 @@ class Map: for x, c in enumerate(line)] for y, line in enumerate(lines)] return Map(width, height, tiles) + def draw_string(self) -> str: + return "\n".join("".join(tile.char for tile in line) for line in self.tiles) + class Tile(Enum): EMPTY = auto() From 1054d8e57ab9953c8e1d665724088e3ba28d7cdb Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 16:41:38 +0200 Subject: [PATCH 11/23] Get a tile from its representation --- dungeonbattle/interfaces.py | 16 ++++++++++------ dungeonbattle/interfaces_test.py | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index ae4937c..99eeeee 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from enum import Enum, auto +from enum import Enum @@ -31,17 +31,21 @@ class Map: return Map(width, height, tiles) def draw_string(self) -> str: - return "\n".join("".join(tile.char for tile in line) for line in self.tiles) + return "\n".join("".join(tile.value for tile in line) + for line in self.tiles) class Tile(Enum): - EMPTY = auto() - WALL = auto() - FLOOR = auto() + EMPTY = ' ' + WALL = '#' + FLOOR = '.' @staticmethod def from_char(c: str): - return {'#': Tile.WALL, '.': Tile.FLOOR, ' ': Tile.EMPTY}[c] + for t in Tile: + if t.value == c: + return t + raise ValueError(f"Tile not found : {c}") class Entity: diff --git a/dungeonbattle/interfaces_test.py b/dungeonbattle/interfaces_test.py index 479a9ac..dc8932a 100644 --- a/dungeonbattle/interfaces_test.py +++ b/dungeonbattle/interfaces_test.py @@ -5,7 +5,7 @@ from dungeonbattle.interfaces import Map class TestInterfaces(unittest.TestCase): def test_map(self) -> None: - m = Map.load_from_string("ab\ncd\n") + m = Map.load_from_string(".#\n#.\n") self.assertEqual(m.width, 2) self.assertEqual(m.height, 2) - self.assertEqual(m.draw_string(), "ab\ncd") + self.assertEqual(m.draw_string(), ".#\n#.") From cadc8c161e4fc6195aeec732c318eda22c2e6d9a Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 16:46:40 +0200 Subject: [PATCH 12/23] Use Map interface in proof of concept --- dungeonbattle/proof_of_concept.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/dungeonbattle/proof_of_concept.py b/dungeonbattle/proof_of_concept.py index 046d687..fd3b85f 100644 --- a/dungeonbattle/proof_of_concept.py +++ b/dungeonbattle/proof_of_concept.py @@ -1,19 +1,15 @@ #!/usr/bin/env python +from .interfaces import Map from .term_manager import TermManager def proof_of_concept() -> None: - matrix = [] - with open("example_map.txt") as f: - for line in f.readlines(): - matrix.append(line[:-1]) + m = Map.load("example_map.txt") with TermManager() as term_manager: stdscr = term_manager.screen - for y in range(len(matrix)): - for x in range(len(matrix[0])): - stdscr.addstr(y, x, matrix[y][x]) + stdscr.addstr(0, 0, m.draw_string()) stdscr.refresh() cur = [1, 6] # (y,x) @@ -24,17 +20,17 @@ def proof_of_concept() -> None: key = stdscr.getkey() stdscr.addstr(cur[0], cur[1], '.') if key == 'z' or key == 'KEY_UP': - if cur[0] > 0 and matrix[cur[0] - 1][cur[1]] != '#': + if cur[0] > 0 and m.tiles[cur[0] - 1][cur[1]].value != '#': cur[0] = cur[0] - 1 if key == 's' or key == 'KEY_DOWN': - if cur[0] < len(matrix) - 1 and \ - matrix[cur[0] + 1][cur[1]] != '#': + if cur[0] < m.height - 1 and \ + m.tiles[cur[0] + 1][cur[1]].value != '#': cur[0] = cur[0] + 1 if key == 'q' or key == 'KEY_LEFT': - if cur[1] > 0 and matrix[cur[0]][cur[1] - 1] != '#': + if cur[1] > 0 and m.tiles[cur[0]][cur[1] - 1].value != '#': cur[1] = cur[1] - 1 if key == 'd' or key == 'KEY_RIGHT': - if cur[1] < len(matrix[0]) and \ - matrix[cur[0]][cur[1] + 1] != '#': + if cur[1] < m.width and \ + m.tiles[cur[0]][cur[1] + 1].value != '#': cur[1] = cur[1] + 1 stdscr.addstr(cur[0], cur[1], '@') From ce0d5d8ffd4a50e399bfe50eb86e3b5074d1a9b1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 17:15:17 +0200 Subject: [PATCH 13/23] :squirrel: Main character is a squirrel --- dungeonbattle/proof_of_concept.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/proof_of_concept.py b/dungeonbattle/proof_of_concept.py index fd3b85f..8cecc64 100644 --- a/dungeonbattle/proof_of_concept.py +++ b/dungeonbattle/proof_of_concept.py @@ -13,7 +13,7 @@ def proof_of_concept() -> None: stdscr.refresh() cur = [1, 6] # (y,x) - stdscr.addstr(1, 6, '@') + stdscr.addstr(1, 6, 'šŸæļø') stdscr.refresh() while True: @@ -33,4 +33,4 @@ def proof_of_concept() -> None: if cur[1] < m.width and \ m.tiles[cur[0]][cur[1] + 1].value != '#': cur[1] = cur[1] + 1 - stdscr.addstr(cur[0], cur[1], '@') + stdscr.addstr(cur[0], cur[1], 'šŸæļø') From 9c6f22ccf8c558a8fe48ea10f7f08ffc2d7db572 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 17:41:37 +0200 Subject: [PATCH 14/23] Better usage of enumerations --- dungeonbattle/interfaces.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 99eeeee..2d4216c 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -26,7 +26,7 @@ class Map: lines = [line for line in lines if line] height = len(lines) width = len(lines[0]) - tiles = [[Tile.from_char(c) + tiles = [[Tile(c) for x, c in enumerate(line)] for y, line in enumerate(lines)] return Map(width, height, tiles) @@ -40,13 +40,6 @@ class Tile(Enum): WALL = '#' FLOOR = '.' - @staticmethod - def from_char(c: str): - for t in Tile: - if t.value == c: - return t - raise ValueError(f"Tile not found : {c}") - class Entity: x: int From d8401d9920f7ce614d6a1e99f2f2c839f2fc1f80 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 17:47:52 +0200 Subject: [PATCH 15/23] More modularity, add properties in tiles --- dungeonbattle/interfaces.py | 6 ++++++ dungeonbattle/proof_of_concept.py | 19 +++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 2d4216c..94bfdad 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -40,6 +40,12 @@ class Tile(Enum): WALL = '#' FLOOR = '.' + def is_wall(self) -> bool: + return self == Tile.WALL + + def can_walk(self) -> bool: + return not self.is_wall() + class Entity: x: int diff --git a/dungeonbattle/proof_of_concept.py b/dungeonbattle/proof_of_concept.py index 8cecc64..8db5aeb 100644 --- a/dungeonbattle/proof_of_concept.py +++ b/dungeonbattle/proof_of_concept.py @@ -19,18 +19,17 @@ def proof_of_concept() -> None: while True: key = stdscr.getkey() stdscr.addstr(cur[0], cur[1], '.') + next_pos = cur[:] if key == 'z' or key == 'KEY_UP': - if cur[0] > 0 and m.tiles[cur[0] - 1][cur[1]].value != '#': - cur[0] = cur[0] - 1 + next_pos[0] = cur[0] - 1 if key == 's' or key == 'KEY_DOWN': - if cur[0] < m.height - 1 and \ - m.tiles[cur[0] + 1][cur[1]].value != '#': - cur[0] = cur[0] + 1 + next_pos[0] = cur[0] + 1 if key == 'q' or key == 'KEY_LEFT': - if cur[1] > 0 and m.tiles[cur[0]][cur[1] - 1].value != '#': - cur[1] = cur[1] - 1 + next_pos[1] = cur[1] - 1 if key == 'd' or key == 'KEY_RIGHT': - if cur[1] < m.width and \ - m.tiles[cur[0]][cur[1] + 1].value != '#': - cur[1] = cur[1] + 1 + next_pos[1] = cur[1] + 1 + if 0 <= next_pos[0] < m.height and 0 <= next_pos[0] < m.width: + next_tile = m.tiles[next_pos[0]][next_pos[1]] + if next_tile.can_walk(): + cur = next_pos stdscr.addstr(cur[0], cur[1], 'šŸæļø') From d8bd500349dbf25fb8dda4e4a26b203c666010d4 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 18:05:49 +0200 Subject: [PATCH 16/23] Add some comments --- dungeonbattle/interfaces.py | 17 +++++++++++++++++ dungeonbattle/interfaces_test.py | 3 +++ dungeonbattle/proof_of_concept.py | 12 +++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 94bfdad..dc0e282 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -4,6 +4,10 @@ from enum import Enum class Map: + """ + Object that represents a Map with its width, height + and the whole tiles, with their custom properties. + """ width: int height: int tiles: list @@ -16,12 +20,18 @@ class Map: @staticmethod def load(filename: str): + """ + Read a file that contains the content of a map, and build a Map object. + """ with open(filename, "r") as f: file = f.read() return Map.load_from_string(file) @staticmethod def load_from_string(content: str): + """ + Load a map represented by its characters and build a Map object. + """ lines = content.split("\n") lines = [line for line in lines if line] height = len(lines) @@ -31,6 +41,10 @@ class Map: return Map(width, height, tiles) def draw_string(self) -> str: + """ + Draw the current map as a string object that can be rendered + in the window. + """ return "\n".join("".join(tile.value for tile in line) for line in self.tiles) @@ -44,6 +58,9 @@ class Tile(Enum): return self == Tile.WALL def can_walk(self) -> bool: + """ + Check if an entity (player or not) can move in this tile. + """ return not self.is_wall() diff --git a/dungeonbattle/interfaces_test.py b/dungeonbattle/interfaces_test.py index dc8932a..34a50f9 100644 --- a/dungeonbattle/interfaces_test.py +++ b/dungeonbattle/interfaces_test.py @@ -5,6 +5,9 @@ from dungeonbattle.interfaces import Map class TestInterfaces(unittest.TestCase): def test_map(self) -> None: + """ + Create a map and check that it is well parsed. + """ m = Map.load_from_string(".#\n#.\n") self.assertEqual(m.width, 2) self.assertEqual(m.height, 2) diff --git a/dungeonbattle/proof_of_concept.py b/dungeonbattle/proof_of_concept.py index 8db5aeb..2034cbf 100644 --- a/dungeonbattle/proof_of_concept.py +++ b/dungeonbattle/proof_of_concept.py @@ -1,22 +1,29 @@ -#!/usr/bin/env python from .interfaces import Map from .term_manager import TermManager def proof_of_concept() -> None: + """ + Read an example map, parse it, then the (squirrel) player can move freely. + """ + # Load the example map m = Map.load("example_map.txt") + # Create the context manager to manipulate the screen with TermManager() as term_manager: stdscr = term_manager.screen + # Draw the full map stdscr.addstr(0, 0, m.draw_string()) stdscr.refresh() cur = [1, 6] # (y,x) + # We are a squirrel stdscr.addstr(1, 6, 'šŸæļø') stdscr.refresh() while True: + # Get movements of the user key = stdscr.getkey() stdscr.addstr(cur[0], cur[1], '.') next_pos = cur[:] @@ -28,8 +35,11 @@ def proof_of_concept() -> None: next_pos[1] = cur[1] - 1 if key == 'd' or key == 'KEY_RIGHT': next_pos[1] = cur[1] + 1 + # Check if we stay in the bounds if 0 <= next_pos[0] < m.height and 0 <= next_pos[0] < m.width: next_tile = m.tiles[next_pos[0]][next_pos[1]] + # Check if the new position is valid if next_tile.can_walk(): cur = next_pos + # Draw the squirrel stdscr.addstr(cur[0], cur[1], 'šŸæļø') From 3467fb622c0a3bdab0d662254739289fed64ae64 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 18:20:26 +0200 Subject: [PATCH 17/23] Replace # by walls --- dungeonbattle/interfaces.py | 2 +- dungeonbattle/proof_of_concept.py | 8 ++++---- example_map.txt | 34 +++++++++++++++---------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index dc0e282..762abbf 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -51,7 +51,7 @@ class Map: class Tile(Enum): EMPTY = ' ' - WALL = '#' + WALL = 'ā–ˆ' FLOOR = '.' def is_wall(self) -> bool: diff --git a/dungeonbattle/proof_of_concept.py b/dungeonbattle/proof_of_concept.py index 2034cbf..9bd2dea 100644 --- a/dungeonbattle/proof_of_concept.py +++ b/dungeonbattle/proof_of_concept.py @@ -1,4 +1,4 @@ -from .interfaces import Map +from .interfaces import Map, Tile from .term_manager import TermManager @@ -19,13 +19,13 @@ def proof_of_concept() -> None: cur = [1, 6] # (y,x) # We are a squirrel - stdscr.addstr(1, 6, 'šŸæļø') + stdscr.addstr(1, 6, 'šŸæ') stdscr.refresh() while True: # Get movements of the user key = stdscr.getkey() - stdscr.addstr(cur[0], cur[1], '.') + stdscr.addstr(cur[0], cur[1], Tile.FLOOR.value) next_pos = cur[:] if key == 'z' or key == 'KEY_UP': next_pos[0] = cur[0] - 1 @@ -42,4 +42,4 @@ def proof_of_concept() -> None: if next_tile.can_walk(): cur = next_pos # Draw the squirrel - stdscr.addstr(cur[0], cur[1], 'šŸæļø') + stdscr.addstr(cur[0], cur[1], 'šŸæ') diff --git a/example_map.txt b/example_map.txt index 4111fae..bc0c464 100644 --- a/example_map.txt +++ b/example_map.txt @@ -1,17 +1,17 @@ - ####### ############# - #.....# #...........# - #.....# #####...........# - #.....# #...............# - #.##### #.###...........# - #.# #.# #...........# - #.# #.# ############# - #.# #.# - #.#### #.# - #....# #.# - ####.###################.# - #.....................# ################# - #.....................# #...............# - #.....................#######...............# - #...........................................# - #.....................#######...............# - ####################### ################# + ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ + ā–ˆ.....ā–ˆ ā–ˆ...........ā–ˆ + ā–ˆ.....ā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆ...........ā–ˆ + ā–ˆ.....ā–ˆ ā–ˆ...............ā–ˆ + ā–ˆ.ā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆ.ā–ˆā–ˆā–ˆ...........ā–ˆ + ā–ˆ.ā–ˆ ā–ˆ.ā–ˆ ā–ˆ...........ā–ˆ + ā–ˆ.ā–ˆ ā–ˆ.ā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ + ā–ˆ.ā–ˆ ā–ˆ.ā–ˆ + ā–ˆ.ā–ˆā–ˆā–ˆā–ˆ ā–ˆ.ā–ˆ + ā–ˆ....ā–ˆ ā–ˆ.ā–ˆ + ā–ˆā–ˆā–ˆā–ˆ.ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ.ā–ˆ + ā–ˆ.....................ā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ + ā–ˆ.....................ā–ˆ ā–ˆ...............ā–ˆ + ā–ˆ.....................ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ...............ā–ˆ + ā–ˆ...........................................ā–ˆ + ā–ˆ.....................ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ...............ā–ˆ + ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ From d264fb7dfe080dd5ae8396aa6b2df7d5774dc329 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 16 Oct 2020 18:22:20 +0200 Subject: [PATCH 18/23] Test got broken --- dungeonbattle/interfaces_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/interfaces_test.py b/dungeonbattle/interfaces_test.py index 34a50f9..849e28b 100644 --- a/dungeonbattle/interfaces_test.py +++ b/dungeonbattle/interfaces_test.py @@ -8,7 +8,7 @@ class TestInterfaces(unittest.TestCase): """ Create a map and check that it is well parsed. """ - m = Map.load_from_string(".#\n#.\n") + m = Map.load_from_string(".ā–ˆ\nā–ˆ.\n") self.assertEqual(m.width, 2) self.assertEqual(m.height, 2) - self.assertEqual(m.draw_string(), ".#\n#.") + self.assertEqual(m.draw_string(), ".ā–ˆ\nā–ˆ.") From 6c675fbac9add7052bac20e0312cc0d11628ce11 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 23 Oct 2020 15:15:37 +0200 Subject: [PATCH 19/23] added player actions for going up, down, left and right --- dungeonbattle/entities/player.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/entities/player.py b/dungeonbattle/entities/player.py index 66aed0c..98797bd 100644 --- a/dungeonbattle/entities/player.py +++ b/dungeonbattle/entities/player.py @@ -1,5 +1,27 @@ -from ..interfaces import FightingEntity +from .interfaces import FightingEntity class Player(FightingEntity): maxhealth = 20 - strength = 5 \ No newline at end of file + strength = 5 + + + def move_up(self, p : Player, m : Map) : + if p.y > 0 : + cell = Tile(Map.tiles[p.y-1][p.x]) + if can_walk(cell) : + p.y = p.y-1 + def move_down(self, p : Player, m : Map) : + if p.y < Map.height : + cell = Tile(Map.tiles[p.y+1][p.x]) + if can_walk(cell) : + p.y = p.y+1 + def move_left(self, p : Player, m : Map) : + if p.x > 0 : + cell = Tile(Map.tiles[p.y][p.x-1]) + if can_walk(cell) : + p.x = p.x-1 + def move_right(self, p : Player, m : Map) : + if p.x < Map.width : + cell = Tile(Map.tiles[p.y][p.x+1]) + if can_walk(cell) : + p.x = p.x+1 From 230a7ec432791d496250e93bfcf98ceaa8992169 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 23 Oct 2020 15:55:30 +0200 Subject: [PATCH 20/23] Cleaned an error in interfaces.py --- dungeonbattle/interfaces.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 12cdaf7..934f290 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -37,11 +37,9 @@ class Map: width = len(lines[0]) tiles = [[Tile(c) for x, c in enumerate(line)] for y, line in enumerate(lines)] -<<<<<<< HEAD - return Map(width, height, tiles) -======= + return Map(width, height, tiles, []) ->>>>>>> master + def draw_string(self) -> str: """ From 7c1c4278927d4d47a3e34e8184b7b65d05487174 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 23 Oct 2020 16:17:48 +0200 Subject: [PATCH 21/23] Deleted unwanted code in Player It isn't the player's role to check whether they can move to a tile, check it beforehands and use the move method from its Entity superclass. --- dungeonbattle/entities/player.py | 24 +----------------------- dungeonbattle/interfaces.py | 2 +- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/dungeonbattle/entities/player.py b/dungeonbattle/entities/player.py index 98797bd..b687366 100644 --- a/dungeonbattle/entities/player.py +++ b/dungeonbattle/entities/player.py @@ -1,27 +1,5 @@ -from .interfaces import FightingEntity +from ..interfaces import FightingEntity class Player(FightingEntity): maxhealth = 20 strength = 5 - - - def move_up(self, p : Player, m : Map) : - if p.y > 0 : - cell = Tile(Map.tiles[p.y-1][p.x]) - if can_walk(cell) : - p.y = p.y-1 - def move_down(self, p : Player, m : Map) : - if p.y < Map.height : - cell = Tile(Map.tiles[p.y+1][p.x]) - if can_walk(cell) : - p.y = p.y+1 - def move_left(self, p : Player, m : Map) : - if p.x > 0 : - cell = Tile(Map.tiles[p.y][p.x-1]) - if can_walk(cell) : - p.x = p.x-1 - def move_right(self, p : Player, m : Map) : - if p.x < Map.width : - cell = Tile(Map.tiles[p.y][p.x+1]) - if can_walk(cell) : - p.x = p.x+1 diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 934f290..e2be5df 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -11,7 +11,7 @@ class Map: height: int tiles: list - def __init__(self, width: int, height: int, tiles: list, entities = []: list): + def __init__(self, width: int, height: int, tiles: list, entities = []): self.width = width self.height = height self.tiles = tiles From 343e107b86f876704b9ba27f4b2daaaeb297924d Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 23 Oct 2020 16:51:48 +0200 Subject: [PATCH 22/23] Basic item and monster interface --- dungeonbattle/entities/items.py | 15 +++++++++++++++ dungeonbattle/entities/monsters.py | 6 +++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 dungeonbattle/entities/items.py diff --git a/dungeonbattle/entities/items.py b/dungeonbattle/entities/items.py new file mode 100644 index 0000000..e88a36e --- /dev/null +++ b/dungeonbattle/entities/items.py @@ -0,0 +1,15 @@ +from ..interfaces import Entity + +class Item(Entity): + held:bool + + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + self.held = False + + def drop(self, x:int, y:int): + self.held = False + self.move(x, y) + + def hold(self): + self.held = True diff --git a/dungeonbattle/entities/monsters.py b/dungeonbattle/entities/monsters.py index c9cdf1c..6f855af 100644 --- a/dungeonbattle/entities/monsters.py +++ b/dungeonbattle/entities/monsters.py @@ -1,5 +1,9 @@ from ..interfaces import FightingEntity -class Squirrel(FightingEntity): +class Monster(FightingEntity): + def behaviour(self, map): + pass + +class Squirrel(Monster): maxhealth = 10 strength = 3 From 2ba7330ff5a91bdf96d58dc1328002418b94a4c9 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 23 Oct 2020 18:02:57 +0200 Subject: [PATCH 23/23] An example of item --- dungeonbattle/entities/items.py | 20 +++++++++++++++++++- dungeonbattle/entities/monsters.py | 2 +- dungeonbattle/interfaces.py | 3 +++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/entities/items.py b/dungeonbattle/entities/items.py index e88a36e..23a1c6e 100644 --- a/dungeonbattle/entities/items.py +++ b/dungeonbattle/entities/items.py @@ -1,4 +1,4 @@ -from ..interfaces import Entity +from ..interfaces import Entity, FightingEntity class Item(Entity): held:bool @@ -13,3 +13,21 @@ class Item(Entity): def hold(self): self.held = True + +class Bomb(Item): + damage:int = 5 + exploding:bool + + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + self.exploding = False + + def drop(self, x:int, y:int): + super.drop(self, x, y) + self.exploding = True + + def act(self, map): + if self.exploding: + for e in map.entities: + if abs (e.x - self.x) + abs (e.y - self.y) <= 1 and isinstance(e,FightingEntity): + e.take_damage(self, self.damage) diff --git a/dungeonbattle/entities/monsters.py b/dungeonbattle/entities/monsters.py index 6f855af..9aa6c06 100644 --- a/dungeonbattle/entities/monsters.py +++ b/dungeonbattle/entities/monsters.py @@ -1,7 +1,7 @@ from ..interfaces import FightingEntity class Monster(FightingEntity): - def behaviour(self, map): + def act(self, map): pass class Squirrel(Monster): diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index e2be5df..f1474e0 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -72,6 +72,9 @@ class Entity: def move(self, x: int, y: int) -> None: self.x = x self.y = y + + def act(self, m:Map): + pass class FightingEntity(Entity): maxhealth: int