From 31ae39ab155244d134f8a093eacd447e0ba6533c Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 6 Nov 2020 15:34:24 +0100 Subject: [PATCH 01/56] basic display wrapper --- dungeonbattle/display/__init__.py | 0 dungeonbattle/display/display.py | 11 ++++++++++ dungeonbattle/display/mapdisplay.py | 31 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 dungeonbattle/display/__init__.py create mode 100644 dungeonbattle/display/display.py create mode 100644 dungeonbattle/display/mapdisplay.py diff --git a/dungeonbattle/display/__init__.py b/dungeonbattle/display/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py new file mode 100644 index 0000000..86c76b1 --- /dev/null +++ b/dungeonbattle/display/display.py @@ -0,0 +1,11 @@ +import curses +from .mapdisplay import MapDisplay + +class Display: + def __init__(self, game, screen): + self.screen = screen + self.game = game + self.mapDisplay = MapDisplay(game.m, curses.LINES, curses.COLS * 4/5) + + def refresh(self): + self.mapDisplay.refresh() \ No newline at end of file diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py new file mode 100644 index 0000000..471aa91 --- /dev/null +++ b/dungeonbattle/display/mapdisplay.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +import curses +from dungeonbattle.interfaces import Map + +class MapDisplay: + + def __init__(self, m: Map, height: int, width: int): + self.width = width + self.height = height + self.map = m + self.pad = curses.newpad(m.height, m.width+1) + + def update_pad(self): + self.pad.addstr(0, 0, self.map.draw_string()) + for e in self.map.entities: + self.pad.addch(e.y, e.x, e.img) + + def display(self, y, x): + deltay, deltax = (self.height // 2) + 1, (self.width //2) + 1 + pminrow, pmincol = y-deltay, x-deltax + sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) + deltay, deltax = self.height - deltay, self.width - deltax + smaxrow = self.map.height - (y + deltay) + self.height -1 + smaxrow = min(smaxrow, self.height-1) + smaxcol = self.map.width - (x + deltax) + self.width -1 + smaxcol = min(smaxcol, self.width-1) + pminrow = max(0, min(self.map.height, pminrow)) + pmincol = max(0, min(self.map.width, pmincol)) + self.pad.clear() + self.update_pad() + self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) From d43a1de2c34d9107af4acb630656425fc09caf37 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 6 Nov 2020 15:58:07 +0100 Subject: [PATCH 02/56] Moved texturepacks into display --- dungeonbattle/{ => display}/texturepack.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dungeonbattle/{ => display}/texturepack.py (100%) diff --git a/dungeonbattle/texturepack.py b/dungeonbattle/display/texturepack.py similarity index 100% rename from dungeonbattle/texturepack.py rename to dungeonbattle/display/texturepack.py From 6bfb8c080cc0b018c44dd3c929e9f31b2755d1b5 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 16:05:01 +0100 Subject: [PATCH 03/56] added name to entities --- dungeonbattle/interfaces.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index f1474e0..f54dd16 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -68,6 +68,7 @@ class Tile(Enum): class Entity: x: int y: int + name : str def move(self, x: int, y: int) -> None: self.x = x From 420ebe79f57784f9752b606b370d0db136c8ccb6 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 16:10:46 +0100 Subject: [PATCH 04/56] passed settings to mapDisplay call --- dungeonbattle/display/display.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 86c76b1..f4359f1 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -5,7 +5,7 @@ class Display: def __init__(self, game, screen): self.screen = screen self.game = game - self.mapDisplay = MapDisplay(game.m, curses.LINES, curses.COLS * 4/5) + self.mapDisplay = MapDisplay(game.m, game.settings, curses.LINES, curses.COLS * 4/5) def refresh(self): - self.mapDisplay.refresh() \ No newline at end of file + self.mapDisplay.refresh() From bcdbd0a47123fc97f2aaf4a8b9c1bd558759e86e Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 16:16:30 +0100 Subject: [PATCH 05/56] changed the name of the texturepack --- dungeonbattle/display/texturepack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dungeonbattle/display/texturepack.py b/dungeonbattle/display/texturepack.py index 3a4eb99..ea3b1e4 100644 --- a/dungeonbattle/display/texturepack.py +++ b/dungeonbattle/display/texturepack.py @@ -1,6 +1,6 @@ #This is the base ascii texturepack -ascii = { +ascii_textures = { "EMPTY" : ' ', "WALL" : '#', "FLOOR" : '.', From 85a006e7b608ce6db56be57c291934370c444a37 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 16:38:32 +0100 Subject: [PATCH 06/56] added coordinates of camera view in map --- dungeonbattle/interfaces.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index f54dd16..ee4d6a3 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -10,6 +10,8 @@ class Map: width: int height: int tiles: list + currentx : int #coordinates of the point that should be on the topleft corner of the screen + currenty : int def __init__(self, width: int, height: int, tiles: list, entities = []): self.width = width From 3d98eac74b9834e5c706f10521ab0e15d7f827ec Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 16:43:07 +0100 Subject: [PATCH 07/56] Added refresh function --- dungeonbattle/display/mapdisplay.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 471aa91..8614821 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -1,19 +1,23 @@ #!/usr/bin/env python import curses from dungeonbattle.interfaces import Map +from dungeonbattle.settings import Settings +import dungeonbattle.texturepack class MapDisplay: - def __init__(self, m: Map, height: int, width: int): + def __init__(self, m: Map, settings : Settings, height: int, width: int): self.width = width self.height = height self.map = m self.pad = curses.newpad(m.height, m.width+1) + if Settings.TEXTURE_PACK == 'ASCII' + self.textures = ascii_textures def update_pad(self): self.pad.addstr(0, 0, self.map.draw_string()) for e in self.map.entities: - self.pad.addch(e.y, e.x, e.img) + self.pad.addch(e.y, e.x, self.textures[e.name]) def display(self, y, x): deltay, deltax = (self.height // 2) + 1, (self.width //2) + 1 @@ -29,3 +33,7 @@ class MapDisplay: self.pad.clear() self.update_pad() self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) + + def refresh(self) : + self.display(self.map.currenty,self.map.currentx) + From 38ed5259d550ef3dfb7ddcf4fbf9b051fdb15805 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 16:50:18 +0100 Subject: [PATCH 08/56] added a squirrel unicode texturepack --- dungeonbattle/display/texturepack.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/texturepack.py b/dungeonbattle/display/texturepack.py index ea3b1e4..3805b44 100644 --- a/dungeonbattle/display/texturepack.py +++ b/dungeonbattle/display/texturepack.py @@ -1,8 +1,13 @@ -#This is the base ascii texturepack - ascii_textures = { "EMPTY" : ' ', "WALL" : '#', "FLOOR" : '.', "PLAYER" : '@' } + +squirrel_textures = { + "EMPTY" : ' ', + "WALL" : 'β–ˆ', + "FLOOR" : '.', + "PLAYER" : '🐿️' + } From 4a54609bad7ed261690179b7e070026660bea4ff Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 17:03:06 +0100 Subject: [PATCH 09/56] changed a few errors --- dungeonbattle/display/mapdisplay.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 8614821..45b65d3 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -2,7 +2,7 @@ import curses from dungeonbattle.interfaces import Map from dungeonbattle.settings import Settings -import dungeonbattle.texturepack +import dungeonbattle.texturepack as tp class MapDisplay: @@ -11,13 +11,15 @@ class MapDisplay: self.height = height self.map = m self.pad = curses.newpad(m.height, m.width+1) - if Settings.TEXTURE_PACK == 'ASCII' - self.textures = ascii_textures + if self.settings.TEXTURE_PACK == 'ASCII' + self.textures = tp.ascii_textures + if self.settings.TEXTURE_PACK == 'SQUIRREL' + self.textures = tp.squirrel_textures def update_pad(self): self.pad.addstr(0, 0, self.map.draw_string()) for e in self.map.entities: - self.pad.addch(e.y, e.x, self.textures[e.name]) + self.pad.addstr(e.y, e.x, self.textures[e.name]) def display(self, y, x): deltay, deltax = (self.height // 2) + 1, (self.width //2) + 1 From a6b93cacfbc4bcfe1717b34e1b5d504da3af2dda Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 17:04:42 +0100 Subject: [PATCH 10/56] Changed Tiles to take into account the texturepack --- dungeonbattle/interfaces.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index ee4d6a3..bedf121 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -1,6 +1,7 @@ #!/usr/bin/env python from enum import Enum - +from dungeonbattle.settings import Settings +import dungeonbattle.texturepack as tp class Map: """ @@ -53,9 +54,14 @@ class Map: class Tile(Enum): - EMPTY = ' ' - WALL = 'β–ˆ' - FLOOR = '.' + if self.settings.TEXTURE_PACK == 'ASCII' + self.textures = tp.ascii_textures + if self.settings.TEXTURE_PACK == 'SQUIRREL' + self.textures = tp.squirrel_textures + + EMPTY = self.textures["EMPTY"] + WALL = self.textures["WALL"] + FLOOR = self.textures["FLOOR"] def is_wall(self) -> bool: return self == Tile.WALL From 3502acd8d65caaa55c6126aa80631db9e6582851 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 17:09:39 +0100 Subject: [PATCH 11/56] put back the ":" where it belongs --- dungeonbattle/display/mapdisplay.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 45b65d3..80a40d3 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -11,9 +11,9 @@ class MapDisplay: self.height = height self.map = m self.pad = curses.newpad(m.height, m.width+1) - if self.settings.TEXTURE_PACK == 'ASCII' + if self.settings.TEXTURE_PACK == 'ASCII' : self.textures = tp.ascii_textures - if self.settings.TEXTURE_PACK == 'SQUIRREL' + if self.settings.TEXTURE_PACK == 'SQUIRREL' : self.textures = tp.squirrel_textures def update_pad(self): From fe77f903d1aa583242c6fc487ae2e055c157120b Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 17:10:38 +0100 Subject: [PATCH 12/56] put back the ":" where it belongs --- dungeonbattle/interfaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index bedf121..e0371b8 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -54,9 +54,9 @@ class Map: class Tile(Enum): - if self.settings.TEXTURE_PACK == 'ASCII' + if self.settings.TEXTURE_PACK == 'ASCII' : self.textures = tp.ascii_textures - if self.settings.TEXTURE_PACK == 'SQUIRREL' + if self.settings.TEXTURE_PACK == 'SQUIRREL' : self.textures = tp.squirrel_textures EMPTY = self.textures["EMPTY"] From 3e1eca8f3964856fceec914de59f1f0e0b6d7d81 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 17:13:27 +0100 Subject: [PATCH 13/56] import name --- dungeonbattle/display/mapdisplay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 80a40d3..026ac3c 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -2,7 +2,7 @@ import curses from dungeonbattle.interfaces import Map from dungeonbattle.settings import Settings -import dungeonbattle.texturepack as tp +import .texturepack as tp class MapDisplay: From 4115363b746cc184637b2f1bda90dc98a692fde9 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 17:17:10 +0100 Subject: [PATCH 14/56] glargh --- dungeonbattle/interfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index e0371b8..15ac4cc 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from enum import Enum from dungeonbattle.settings import Settings -import dungeonbattle.texturepack as tp +from dungeonbattle.display.texturepack import texturepack class Map: """ From f9dcc8f1c1b96a02ac079629ce068ad447a60a0f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 6 Nov 2020 17:43:30 +0100 Subject: [PATCH 15/56] Texture packs are working --- dungeonbattle/display/display.py | 10 +++++-- dungeonbattle/display/mapdisplay.py | 15 ++++------ dungeonbattle/display/texturepack.py | 45 ++++++++++++++++++++-------- dungeonbattle/interfaces.py | 25 ++++++++-------- dungeonbattle/settings.py | 2 +- dungeonbattle/settings_test.py | 2 +- 6 files changed, 61 insertions(+), 38 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index f4359f1..07db361 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -1,11 +1,17 @@ import curses from .mapdisplay import MapDisplay +from .texturepack import TexturePack + class Display: def __init__(self, game, screen): self.screen = screen self.game = game - self.mapDisplay = MapDisplay(game.m, game.settings, curses.LINES, curses.COLS * 4/5) - + self.mapDisplay = MapDisplay(game.m, + TexturePack.get_pack( + game.settings.TEXTURE_PACK), + curses.LINES, + curses.COLS * 4 // 5) + def refresh(self): self.mapDisplay.refresh() diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 026ac3c..3774f1d 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -1,25 +1,22 @@ #!/usr/bin/env python import curses from dungeonbattle.interfaces import Map -from dungeonbattle.settings import Settings -import .texturepack as tp +from .texturepack import TexturePack + class MapDisplay: - def __init__(self, m: Map, settings : Settings, height: int, width: int): + def __init__(self, m: Map, pack: TexturePack, height: int, width: int): self.width = width self.height = height self.map = m self.pad = curses.newpad(m.height, m.width+1) - if self.settings.TEXTURE_PACK == 'ASCII' : - self.textures = tp.ascii_textures - if self.settings.TEXTURE_PACK == 'SQUIRREL' : - self.textures = tp.squirrel_textures + self.pack = pack def update_pad(self): - self.pad.addstr(0, 0, self.map.draw_string()) + self.pad.addstr(0, 0, self.map.draw_string(self.pack)) for e in self.map.entities: - self.pad.addstr(e.y, e.x, self.textures[e.name]) + self.pad.addstr(e.y, e.x, self.pack.PLAYER) def display(self, y, x): deltay, deltax = (self.height // 2) + 1, (self.width //2) + 1 diff --git a/dungeonbattle/display/texturepack.py b/dungeonbattle/display/texturepack.py index 3805b44..0ed6323 100644 --- a/dungeonbattle/display/texturepack.py +++ b/dungeonbattle/display/texturepack.py @@ -1,13 +1,34 @@ -ascii_textures = { - "EMPTY" : ' ', - "WALL" : '#', - "FLOOR" : '.', - "PLAYER" : '@' - } +class TexturePack: + _packs = dict() -squirrel_textures = { - "EMPTY" : ' ', - "WALL" : 'β–ˆ', - "FLOOR" : '.', - "PLAYER" : '🐿️' - } + name: str + EMPTY: str + WALL: str + FLOOR: str + PLAYER: str + + def __init__(self, name: str, **kwargs): + self.name = name + self.__dict__.update(**kwargs) + TexturePack._packs[name] = self + + @classmethod + def get_pack(cls, name: str) -> "TexturePack": + return cls._packs[name] + + +TexturePack.ASCII_PACK = TexturePack( + name="ascii", + EMPTY=' ', + WALL='#', + FLOOR='.', + PLAYER='@', +) + +TexturePack.SQUIRREL_PACK = TexturePack( + name="squirrel", + EMPTY=' ', + WALL='β–ˆ', + FLOOR='.', + PLAYER='🐿️', +) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 15ac4cc..f494057 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -from enum import Enum -from dungeonbattle.settings import Settings -from dungeonbattle.display.texturepack import texturepack +from enum import Enum, auto + +from dungeonbattle.display.texturepack import TexturePack + class Map: """ @@ -44,24 +45,22 @@ class Map: return Map(width, height, tiles, []) - def draw_string(self) -> str: + def draw_string(self, pack: TexturePack) -> 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) + return "\n".join("".join(tile.char(pack) for tile in line) for line in self.tiles) class Tile(Enum): - if self.settings.TEXTURE_PACK == 'ASCII' : - self.textures = tp.ascii_textures - if self.settings.TEXTURE_PACK == 'SQUIRREL' : - self.textures = tp.squirrel_textures - - EMPTY = self.textures["EMPTY"] - WALL = self.textures["WALL"] - FLOOR = self.textures["FLOOR"] + EMPTY = auto() + WALL = auto() + FLOOR = auto() + + def char(self, pack: TexturePack) -> str: + return getattr(pack, self.name) def is_wall(self) -> bool: return self == Tile.WALL diff --git a/dungeonbattle/settings.py b/dungeonbattle/settings.py index 064ad69..ad24656 100644 --- a/dungeonbattle/settings.py +++ b/dungeonbattle/settings.py @@ -19,7 +19,7 @@ class Settings: self.KEY_LEFT_SECONDARY = ['KEY_LEFT', 'Touche secondaire pour aller vers la gauche'] self.KEY_RIGHT_PRIMARY = ['d', 'Touche principale pour aller vers la droite'] self.KEY_RIGHT_SECONDARY = ['KEY_RIGHT', 'Touche secondaire pour aller vers la droite'] - self.TEXTURE_PACK = ['ASCII', 'Pack de textures utilisΓ©'] + self.TEXTURE_PACK = ['ascii', 'Pack de textures utilisΓ©'] def __getattribute__(self, item: str) -> Any: superattribute = super().__getattribute__(item) diff --git a/dungeonbattle/settings_test.py b/dungeonbattle/settings_test.py index 4841b5f..d8a49f0 100644 --- a/dungeonbattle/settings_test.py +++ b/dungeonbattle/settings_test.py @@ -17,7 +17,7 @@ class TestSettings(unittest.TestCase): self.assertEqual(settings.KEY_DOWN_SECONDARY, 'KEY_DOWN') self.assertEqual(settings.KEY_LEFT_SECONDARY, 'KEY_LEFT') self.assertEqual(settings.KEY_RIGHT_SECONDARY, 'KEY_RIGHT') - self.assertEqual(settings.TEXTURE_PACK, 'ASCII') + self.assertEqual(settings.TEXTURE_PACK, 'ascii') self.assertEqual(settings.get_comment(settings.TEXTURE_PACK), settings.get_comment('TEXTURE_PACK')) self.assertEqual(settings.get_comment(settings.TEXTURE_PACK), 'Pack de textures utilisΓ©') From b1282c107d94ec88208d392d8554dec5110398ff Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 18:08:10 +0100 Subject: [PATCH 16/56] added player carachteristics --- dungeonbattle/interfaces.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index f494057..069d7c5 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -88,6 +88,11 @@ class FightingEntity(Entity): maxhealth: int health: int strength: int + intelligence: int + charisma: int + dexterity: int + constitution: int + level: int def __init__(self): self.health = self.maxhealth From bd9dd122f4447522d61dd8e79199a460aa7319b0 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 18:12:17 +0100 Subject: [PATCH 17/56] Added experience system --- dungeonbattle/entities/player.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dungeonbattle/entities/player.py b/dungeonbattle/entities/player.py index b687366..b717af3 100644 --- a/dungeonbattle/entities/player.py +++ b/dungeonbattle/entities/player.py @@ -3,3 +3,15 @@ from ..interfaces import FightingEntity class Player(FightingEntity): maxhealth = 20 strength = 5 + + currentXP: int + maxXP: int + + def level_up(self): + if currentXP>maxXP : + self.level+=1 + currentXP = 0 + maxXP = self.level*10 + def addXP(self, xp) : + currentXP+=xp + self.level_up() From 1e2ff8a8ea7657329872346f5eb194fd002bb258 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 19:50:26 +0100 Subject: [PATCH 18/56] Class for displaying the player statistics (not yet tested) --- dungeonbattle/display/statsdisplay.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 dungeonbattle/display/statsdisplay.py diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py new file mode 100644 index 0000000..0a5d248 --- /dev/null +++ b/dungeonbattle/display/statsdisplay.py @@ -0,0 +1,13 @@ +class StatsDisplay: + def __init__(self, player, height, width, topleftx, toplefty) : + self.width = width + self.height = height + self.pad = curses.newpad(height, width) + def update_pad(self) : + string = "Player -- LVL {} EXP {}/{} HP {}/{}\nStats : STR {} INT {} CHR {} DEX {} CON {}".format(player.level, player.currentXP, player.maxXP, player.health, player.maxhealth, player.strength, player.intelligence, player.charisma, player.dexterity, player.constitution) + self.pad.addstr(0, 0, string) + def refresh(self) : + self.pad.clear() + self.update_pad() + self.pad.refresh(0, 0, toplefty, topleftx, heigth+toplefty, width+topleftx) + From 760e322389281221446c1429b9306f339ec46b84 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 6 Nov 2020 19:53:27 +0100 Subject: [PATCH 19/56] re-statsdisplay --- dungeonbattle/display/statsdisplay.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 0a5d248..6dc0b11 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -1,3 +1,5 @@ +import curses + class StatsDisplay: def __init__(self, player, height, width, topleftx, toplefty) : self.width = width From b3d789e3e79ac68f8c70b21fe84cc8f36c10cc83 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 6 Nov 2020 20:18:27 +0100 Subject: [PATCH 20/56] Fix texture packs --- dungeonbattle/display/texturepack.py | 3 +++ dungeonbattle/interfaces.py | 9 +++++++- example_map.txt | 34 ++++++++++++++-------------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/dungeonbattle/display/texturepack.py b/dungeonbattle/display/texturepack.py index 0ed6323..fa8cd2d 100644 --- a/dungeonbattle/display/texturepack.py +++ b/dungeonbattle/display/texturepack.py @@ -7,6 +7,9 @@ class TexturePack: FLOOR: str PLAYER: str + ASCII_PACK: "TexturePack" + SQUIRREL_PACK: "TexturePack" + def __init__(self, name: str, **kwargs): self.name = name self.__dict__.update(**kwargs) diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 90b7e9c..2459149 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -46,7 +46,7 @@ class Map: lines = [line for line in lines if line] height = len(lines) width = len(lines[0]) - tiles = [[Tile(c) + tiles = [[Tile.from_ascii_char(c) for x, c in enumerate(line)] for y, line in enumerate(lines)] return Map(width, height, tiles) @@ -65,6 +65,13 @@ class Tile(Enum): WALL = auto() FLOOR = auto() + @classmethod + def from_ascii_char(cls, ch: str) -> "Tile": + for tile in Tile: + if tile.char(TexturePack.ASCII_PACK) == ch: + return tile + raise ValueError(ch) + def char(self, pack: TexturePack) -> str: return getattr(pack, self.name) diff --git a/example_map.txt b/example_map.txt index bc0c464..4111fae 100644 --- a/example_map.txt +++ b/example_map.txt @@ -1,17 +1,17 @@ - β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ - β–ˆ.....β–ˆ β–ˆ...........β–ˆ - β–ˆ.....β–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ...........β–ˆ - β–ˆ.....β–ˆ β–ˆ...............β–ˆ - β–ˆ.β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆ.β–ˆβ–ˆβ–ˆ...........β–ˆ - β–ˆ.β–ˆ β–ˆ.β–ˆ β–ˆ...........β–ˆ - β–ˆ.β–ˆ β–ˆ.β–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ - β–ˆ.β–ˆ β–ˆ.β–ˆ - β–ˆ.β–ˆβ–ˆβ–ˆβ–ˆ β–ˆ.β–ˆ - β–ˆ....β–ˆ β–ˆ.β–ˆ - β–ˆβ–ˆβ–ˆβ–ˆ.β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ.β–ˆ - β–ˆ.....................β–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ - β–ˆ.....................β–ˆ β–ˆ...............β–ˆ - β–ˆ.....................β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ...............β–ˆ - β–ˆ...........................................β–ˆ - β–ˆ.....................β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ...............β–ˆ - β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + ####### ############# + #.....# #...........# + #.....# #####...........# + #.....# #...............# + #.##### #.###...........# + #.# #.# #...........# + #.# #.# ############# + #.# #.# + #.#### #.# + #....# #.# + ####.###################.# + #.....................# ################# + #.....................# #...............# + #.....................#######...............# + #...........................................# + #.....................#######...............# + ####################### ################# From b5b76fd07bc25e19d420df7b3e680c19dc8852cf Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 6 Nov 2020 20:24:19 +0100 Subject: [PATCH 21/56] Make the game start --- dungeonbattle/display/display.py | 13 ++++++++----- dungeonbattle/display/mapdisplay.py | 15 +++++++-------- dungeonbattle/game.py | 6 +++--- main.py | 2 +- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 07db361..de5c280 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -7,11 +7,14 @@ class Display: def __init__(self, game, screen): self.screen = screen self.game = game - self.mapDisplay = MapDisplay(game.m, - TexturePack.get_pack( + self.map_display = MapDisplay(game.m, + TexturePack.get_pack( game.settings.TEXTURE_PACK), - curses.LINES, - curses.COLS * 4 // 5) + curses.LINES, + curses.COLS * 4 // 5) def refresh(self): - self.mapDisplay.refresh() + self.map_display.update_pad() + + def display(self, y, x): + self.map_display.display(y, x) diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 3774f1d..03cf092 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -1,17 +1,20 @@ #!/usr/bin/env python import curses + +from dungeonbattle.display.texturepack import TexturePack from dungeonbattle.interfaces import Map -from .texturepack import TexturePack class MapDisplay: - def __init__(self, m: Map, pack: TexturePack, height: int, width: int): + def __init__(self, m: Map, pack: TexturePack, height: int, width: int, + init_pad: bool = True): self.width = width self.height = height - self.map = m - self.pad = curses.newpad(m.height, m.width+1) self.pack = pack + self.map = m + if init_pad: + self.pad = curses.newpad(m.height, m.width + 1) def update_pad(self): self.pad.addstr(0, 0, self.map.draw_string(self.pack)) @@ -32,7 +35,3 @@ class MapDisplay: self.pad.clear() self.update_pad() self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) - - def refresh(self) : - self.display(self.map.currenty,self.map.currentx) - diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index e0e707d..5445bd2 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -1,9 +1,9 @@ import sys from typing import Any +from .display.display import Display from .entities.player import Player from .interfaces import Map -from .mapdisplay import MapDisplay from .settings import Settings from enum import Enum, auto from . import menus @@ -32,13 +32,13 @@ class Game: self.settings.load_settings() self.settings.write_settings() - def new_game(self, init_pad: bool = True) -> None: + def new_game(self, screen: Any) -> 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) + self.d = Display(self, screen) @staticmethod def load_game(filename: str) -> None: diff --git a/main.py b/main.py index 2e44d9d..40530b9 100755 --- a/main.py +++ b/main.py @@ -5,5 +5,5 @@ from dungeonbattle.term_manager import TermManager if __name__ == "__main__": with TermManager() as term_manager: game = Game() - game.new_game() + game.new_game(term_manager.screen) game.run(term_manager.screen) From dbd5f6a1fd91aafbb106cfaf425363f7192cb832 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 6 Nov 2020 20:26:10 +0100 Subject: [PATCH 22/56] Unicode texture pack is squirrel texture pack --- dungeonbattle/tests/interfaces_test.py | 5 +++-- dungeonbattle/tests/settings_test.py | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dungeonbattle/tests/interfaces_test.py b/dungeonbattle/tests/interfaces_test.py index d6ab078..c90f716 100644 --- a/dungeonbattle/tests/interfaces_test.py +++ b/dungeonbattle/tests/interfaces_test.py @@ -1,5 +1,6 @@ import unittest +from dungeonbattle.display.texturepack import TexturePack from dungeonbattle.interfaces import Map, Tile @@ -8,10 +9,10 @@ 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(TexturePack.ASCII_PACK), ".#\n#.") def test_load_map(self) -> None: """ diff --git a/dungeonbattle/tests/settings_test.py b/dungeonbattle/tests/settings_test.py index 0aebaff..9a56048 100644 --- a/dungeonbattle/tests/settings_test.py +++ b/dungeonbattle/tests/settings_test.py @@ -23,10 +23,10 @@ class TestSettings(unittest.TestCase): self.assertEqual(settings.get_comment(settings.TEXTURE_PACK), 'Pack de textures utilisΓ©') - settings.TEXTURE_PACK = 'UNICODE' - self.assertEqual(settings.TEXTURE_PACK, 'UNICODE') + settings.TEXTURE_PACK = 'squirrel' + self.assertEqual(settings.TEXTURE_PACK, 'squirrel') settings.write_settings() settings.load_settings() - self.assertEqual(settings.TEXTURE_PACK, 'UNICODE') + self.assertEqual(settings.TEXTURE_PACK, 'squirrel') From 2e667cdebeaf7b93a763897a4c8bb25f80dea2dc Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 6 Nov 2020 20:28:10 +0100 Subject: [PATCH 23/56] Fix tests --- dungeonbattle/display/display.py | 5 +++-- dungeonbattle/tests/game_test.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index de5c280..b0ced92 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -7,11 +7,12 @@ class Display: def __init__(self, game, screen): self.screen = screen self.game = game + lines = curses.LINES if screen else 4 + cols = curses.COLS * 4 // 5 if screen else 4 self.map_display = MapDisplay(game.m, TexturePack.get_pack( game.settings.TEXTURE_PACK), - curses.LINES, - curses.COLS * 4 // 5) + lines, cols, screen is not None) def refresh(self): self.map_display.update_pad() diff --git a/dungeonbattle/tests/game_test.py b/dungeonbattle/tests/game_test.py index 4183cbe..f4c1fdf 100644 --- a/dungeonbattle/tests/game_test.py +++ b/dungeonbattle/tests/game_test.py @@ -10,7 +10,7 @@ class TestGame(unittest.TestCase): Setup game. """ self.game = Game() - self.game.new_game(False) + self.game.new_game(None) def test_load_game(self) -> None: self.assertRaises(NotImplementedError, Game.load_game, "game.save") From 0bd26a1bd055dd09ca3b23c4694ab39744cc11b5 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 6 Nov 2020 21:15:09 +0100 Subject: [PATCH 24/56] Linting --- dungeonbattle/display/display.py | 12 ++++++++---- dungeonbattle/display/mapdisplay.py | 18 ++++++++--------- dungeonbattle/display/statsdisplay.py | 28 +++++++++++++++++++++------ dungeonbattle/entities/player.py | 19 +++++++++--------- dungeonbattle/interfaces.py | 8 +++++--- 5 files changed, 54 insertions(+), 31 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index b0ced92..c21db74 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -1,21 +1,25 @@ import curses +from typing import Any + from .mapdisplay import MapDisplay from .texturepack import TexturePack +from ..game import Game class Display: - def __init__(self, game, screen): + def __init__(self, game: Game, screen: Any): self.screen = screen self.game = game lines = curses.LINES if screen else 4 cols = curses.COLS * 4 // 5 if screen else 4 self.map_display = MapDisplay(game.m, TexturePack.get_pack( - game.settings.TEXTURE_PACK), + game.settings.TEXTURE_PACK + ), lines, cols, screen is not None) - def refresh(self): + def refresh(self) -> None: self.map_display.update_pad() - def display(self, y, x): + def display(self, y: int, x: int) -> None: self.map_display.display(y, x) diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 03cf092..3a03290 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -16,21 +16,21 @@ class MapDisplay: if init_pad: self.pad = curses.newpad(m.height, m.width + 1) - def update_pad(self): + def update_pad(self) -> None: self.pad.addstr(0, 0, self.map.draw_string(self.pack)) for e in self.map.entities: self.pad.addstr(e.y, e.x, self.pack.PLAYER) - def display(self, y, x): - deltay, deltax = (self.height // 2) + 1, (self.width //2) + 1 - pminrow, pmincol = y-deltay, x-deltax + def display(self, y: int, x: int) -> None: + deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1 + pminrow, pmincol = y - deltay, x - deltax sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) deltay, deltax = self.height - deltay, self.width - deltax - smaxrow = self.map.height - (y + deltay) + self.height -1 - smaxrow = min(smaxrow, self.height-1) - smaxcol = self.map.width - (x + deltax) + self.width -1 - smaxcol = min(smaxcol, self.width-1) - pminrow = max(0, min(self.map.height, pminrow)) + smaxrow = self.map.height - (y + deltay) + self.height - 1 + smaxrow = min(smaxrow, self.height - 1) + smaxcol = self.map.width - (x + deltax) + self.width - 1 + smaxcol = min(smaxcol, self.width - 1) + pminrow = max(0, min(self.map.height, pminrow)) pmincol = max(0, min(self.map.width, pmincol)) self.pad.clear() self.update_pad() diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 6dc0b11..4ae7ab8 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -1,15 +1,31 @@ import curses +from dungeonbattle.entities.player import Player + + class StatsDisplay: - def __init__(self, player, height, width, topleftx, toplefty) : + def __init__(self, player: Player, height: int, width: int, + topleftx: int, toplefty: int): self.width = width self.height = height + self.topleftx = topleftx + self.toplefty = toplefty + self.player = player self.pad = curses.newpad(height, width) - def update_pad(self) : - string = "Player -- LVL {} EXP {}/{} HP {}/{}\nStats : STR {} INT {} CHR {} DEX {} CON {}".format(player.level, player.currentXP, player.maxXP, player.health, player.maxhealth, player.strength, player.intelligence, player.charisma, player.dexterity, player.constitution) + + def update_pad(self) -> None: + string = "Player -- LVL {} EXP {}/{} HP {}/{}\n" \ + "Stats : STR {} INT {} CHR {} DEX {} CON {}"\ + .format(self.player.level, self.player.current_xp, + self.player.max_xp, self.player.health, + self.player.maxhealth, self.player.strength, + self.player.intelligence, self.player.charisma, + self.player.dexterity, self.player.constitution) self.pad.addstr(0, 0, string) - def refresh(self) : + + def refresh(self) -> None: self.pad.clear() self.update_pad() - self.pad.refresh(0, 0, toplefty, topleftx, heigth+toplefty, width+topleftx) - + self.pad.refresh(0, 0, self.toplefty, self.topleftx, + self.heigth + self.toplefty, + self.width + self.topleftx) diff --git a/dungeonbattle/entities/player.py b/dungeonbattle/entities/player.py index 9a95150..c4fe6b8 100644 --- a/dungeonbattle/entities/player.py +++ b/dungeonbattle/entities/player.py @@ -17,14 +17,15 @@ class Player(FightingEntity): def move_right(self) -> bool: return self.check_move(self.y, self.x + 1, True) - currentXP: int - maxXP: int + current_xp: int + max_xp: int - def level_up(self): - if currentXP>maxXP : - self.level+=1 - currentXP = 0 - maxXP = self.level*10 - def addXP(self, xp) : - currentXP+=xp + def level_up(self) -> None: + if self.current_xp > self.max_xp: + self.level += 1 + self.current_xp = 0 + self.max_xp = self.level * 10 + + def add_xp(self, xp: int) -> None: + self.current_xp += xp self.level_up() diff --git a/dungeonbattle/interfaces.py b/dungeonbattle/interfaces.py index 2459149..bbcd25e 100644 --- a/dungeonbattle/interfaces.py +++ b/dungeonbattle/interfaces.py @@ -12,8 +12,10 @@ class Map: width: int height: int tiles: list - currentx : int #coordinates of the point that should be on the topleft corner of the screen - currenty : int + # coordinates of the point that should be + # on the topleft corner of the screen + currentx: int + currenty: int def __init__(self, width: int, height: int, tiles: list): self.width = width @@ -88,7 +90,7 @@ class Tile(Enum): class Entity: y: int x: int - name : str + name: str map: Map def __init__(self): From 0de11abfa8f97bf3825ee5397adf3b9677fa23dc Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 6 Nov 2020 21:17:31 +0100 Subject: [PATCH 25/56] Fix circulary import --- dungeonbattle/display/display.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index c21db74..d58b772 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -3,11 +3,11 @@ from typing import Any from .mapdisplay import MapDisplay from .texturepack import TexturePack -from ..game import Game class Display: - def __init__(self, game: Game, screen: Any): + # game is a game, can't import to avoid circulary includes + def __init__(self, game: Any, screen: Any): self.screen = screen self.game = game lines = curses.LINES if screen else 4 From aeb43a0cec1e49fd7dd2f38333ff15231b181ae9 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 6 Nov 2020 21:23:17 +0100 Subject: [PATCH 26/56] Test player level up --- dungeonbattle/entities/player.py | 14 +++++++------- dungeonbattle/tests/entities_test.py | 5 +++++ dungeonbattle/tests/interfaces_test.py | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dungeonbattle/entities/player.py b/dungeonbattle/entities/player.py index c4fe6b8..4556f9f 100644 --- a/dungeonbattle/entities/player.py +++ b/dungeonbattle/entities/player.py @@ -2,8 +2,11 @@ from ..interfaces import FightingEntity class Player(FightingEntity): - maxhealth = 20 - strength = 5 + maxhealth: int = 20 + strength: int = 5 + level: int = 1 + current_xp: int = 0 + max_xp: int = 10 def move_up(self) -> bool: return self.check_move(self.y - 1, self.x, True) @@ -17,13 +20,10 @@ class Player(FightingEntity): def move_right(self) -> bool: return self.check_move(self.y, self.x + 1, True) - current_xp: int - max_xp: int - def level_up(self) -> None: - if self.current_xp > self.max_xp: + while self.current_xp > self.max_xp: self.level += 1 - self.current_xp = 0 + self.current_xp -= self.max_xp self.max_xp = self.level * 10 def add_xp(self, xp: int) -> None: diff --git a/dungeonbattle/tests/entities_test.py b/dungeonbattle/tests/entities_test.py index 7b7902b..e143926 100644 --- a/dungeonbattle/tests/entities_test.py +++ b/dungeonbattle/tests/entities_test.py @@ -95,3 +95,8 @@ class TestEntities(unittest.TestCase): self.assertFalse(player.move_right()) self.assertTrue(player.move_down()) self.assertTrue(player.move_down()) + + player.add_xp(70) + self.assertEqual(player.current_xp, 10) + self.assertEqual(player.max_xp, 40) + self.assertEqual(player.level, 4) diff --git a/dungeonbattle/tests/interfaces_test.py b/dungeonbattle/tests/interfaces_test.py index c90f716..56e0e1d 100644 --- a/dungeonbattle/tests/interfaces_test.py +++ b/dungeonbattle/tests/interfaces_test.py @@ -32,3 +32,4 @@ class TestInterfaces(unittest.TestCase): self.assertTrue(Tile.FLOOR.can_walk()) self.assertFalse(Tile.WALL.can_walk()) self.assertTrue(Tile.EMPTY.can_walk()) + self.assertRaises(ValueError, Tile.from_ascii_char, 'unknown') From 4ea3d20b28386eefdfd10ef9f63336d5b41c60db Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sat, 7 Nov 2020 15:00:24 +0100 Subject: [PATCH 27/56] Better stats display (tested) --- dungeonbattle/display/statsdisplay.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 4ae7ab8..8c84936 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -14,18 +14,29 @@ class StatsDisplay: self.pad = curses.newpad(height, width) def update_pad(self) -> None: - string = "Player -- LVL {} EXP {}/{} HP {}/{}\n" \ - "Stats : STR {} INT {} CHR {} DEX {} CON {}"\ + string = "" + for i in range(self.width-1) : + string = string + "-" + string = string + self.pad.addstr(0, 0, string) + string2 = "Player -- LVL {} EXP {}/{} HP {}/{}"\ .format(self.player.level, self.player.current_xp, self.player.max_xp, self.player.health, - self.player.maxhealth, self.player.strength, + self.player.maxhealth) + for i in range(self.width-len(string2)-1) : + string2 = string2 + " " + self.pad.addstr(1, 0, string2) + string3 = "Stats : STR {} INT {} CHR {} DEX {} CON {}"\ + .format(self.player.strength, self.player.intelligence, self.player.charisma, self.player.dexterity, self.player.constitution) - self.pad.addstr(0, 0, string) + for i in range(self.width-len(string3)-1) : + string3 = string3 + " " + self.pad.addstr(2, 0, string3) def refresh(self) -> None: self.pad.clear() self.update_pad() self.pad.refresh(0, 0, self.toplefty, self.topleftx, - self.heigth + self.toplefty, - self.width + self.topleftx) + 2+ self.toplefty, + self.width+ self.topleftx) From e0366b4719ea06b7e431b26666d405da46817206 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 8 Nov 2020 16:36:21 +0100 Subject: [PATCH 28/56] Menu display is now supported --- dungeonbattle/display/menudisplay.py | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 dungeonbattle/display/menudisplay.py diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py new file mode 100644 index 0000000..cf7d0d1 --- /dev/null +++ b/dungeonbattle/display/menudisplay.py @@ -0,0 +1,47 @@ +from dungeonbattle.menus import Menu +from typing import Any +import curses + +class MenuDisplay: + def __init__(self, menu : Menu, screen: Any, height : int, width : int, topleftx: int, toplefty: int) : + self.screen = screen + self.position = menu.position + self.values = menu.values + self.width = width + self.height = height + self.trueheight = len(menu.values) + self.truewidth = max(map(len,self.values)) + self.topleftx = topleftx + self.toplefty = toplefty + + #Menu values are printed in pad + self.pad = curses.newpad(self.trueheight,self.truewidth+1) + for i in range(self.trueheight-1) : + self.pad.addstr(i,0," " + self.values[i]) + + #Menu box + self.menubox = curses.newpad(self.height,self.width) + self.menubox.addstr(0,0,"┏"+"━"*(self.width-2)+"β”“") + for i in range(1,self.height-2) : + self.menubox.addstr(i,0,"┃"+" "*(self.width-2)+"┃") + self.menubox.addstr(self.height-2, 0, "β”—"+"━"*(self.width-2)+"β”›") + + def update_pad(self, position : int) -> None: + for i in range(self.trueheight) : + self.pad.addstr(i,0," ") + self.pad.addstr(position,0,">") #set a marker in front of the selected line + + def refresh(self, position : int) -> None: + if self.height-2>=position-1 : + cornery = 0 + elif self.height-2 >= self.trueheight-position : + cornery = self.trueheight-self.height+2 + + self.menubox.refresh(0, 0, self.toplefty, self.topleftx, + self.height + self.toplefty, + self.width + self.topleftx) + self.update_pad(position) + self.pad.refresh(cornery, 0, self.toplefty+1, self.topleftx+1, + self.height-2 + self.toplefty, + self.width-2 + self.topleftx) + From 933385e79dc8bf6f0642191e297f2b4a070274a1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 22:48:50 +0100 Subject: [PATCH 29/56] Linting --- dungeonbattle/display/menudisplay.py | 59 ++++++++++++++------------- dungeonbattle/display/statsdisplay.py | 12 +++--- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index cf7d0d1..803311d 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -2,46 +2,49 @@ from dungeonbattle.menus import Menu from typing import Any import curses + class MenuDisplay: - def __init__(self, menu : Menu, screen: Any, height : int, width : int, topleftx: int, toplefty: int) : + def __init__(self, menu: Menu, screen: Any, height: int, width: int, + topleftx: int, toplefty: int): self.screen = screen self.position = menu.position self.values = menu.values self.width = width self.height = height self.trueheight = len(menu.values) - self.truewidth = max(map(len,self.values)) + self.truewidth = max(map(len, self.values)) self.topleftx = topleftx self.toplefty = toplefty - #Menu values are printed in pad - self.pad = curses.newpad(self.trueheight,self.truewidth+1) - for i in range(self.trueheight-1) : - self.pad.addstr(i,0," " + self.values[i]) + # Menu values are printed in pad + self.pad = curses.newpad(self.trueheight, self.truewidth + 1) + for i in range(self.trueheight - 1): + self.pad.addstr(i, 0, " " + self.values[i]) - #Menu box - self.menubox = curses.newpad(self.height,self.width) - self.menubox.addstr(0,0,"┏"+"━"*(self.width-2)+"β”“") - for i in range(1,self.height-2) : - self.menubox.addstr(i,0,"┃"+" "*(self.width-2)+"┃") - self.menubox.addstr(self.height-2, 0, "β”—"+"━"*(self.width-2)+"β”›") - - def update_pad(self, position : int) -> None: - for i in range(self.trueheight) : - self.pad.addstr(i,0," ") - self.pad.addstr(position,0,">") #set a marker in front of the selected line + # Menu box + self.menubox = curses.newpad(self.height, self.width) + self.menubox.addstr(0, 0, "┏" + "━" * (self.width - 2) + "β”“") + for i in range(1, self.height - 2): + self.menubox.addstr(i, 0, "┃" + " " * (self.width - 2) + "┃") + self.menubox.addstr(self.height - 2, 0, + "β”—" + "━" * (self.width - 2) + "β”›") - def refresh(self, position : int) -> None: - if self.height-2>=position-1 : + def update_pad(self, position: int) -> None: + for i in range(self.trueheight): + self.pad.addstr(i, 0, " ") + # set a marker in front of the selected line + self.pad.addstr(position, 0, ">") + + def refresh(self, position: int) -> None: + if self.height - 2 >= position - 1: cornery = 0 - elif self.height-2 >= self.trueheight-position : - cornery = self.trueheight-self.height+2 - + elif self.height - 2 >= self.trueheight - position: + cornery = self.trueheight - self.height + 2 + self.menubox.refresh(0, 0, self.toplefty, self.topleftx, - self.height + self.toplefty, - self.width + self.topleftx) + self.height + self.toplefty, + self.width + self.topleftx) self.update_pad(position) - self.pad.refresh(cornery, 0, self.toplefty+1, self.topleftx+1, - self.height-2 + self.toplefty, - self.width-2 + self.topleftx) - + self.pad.refresh(cornery, 0, self.toplefty + 1, self.topleftx + 1, + self.height - 2 + self.toplefty, + self.width - 2 + self.topleftx) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 8c84936..b6f17dc 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -15,7 +15,7 @@ class StatsDisplay: def update_pad(self) -> None: string = "" - for i in range(self.width-1) : + for i in range(self.width - 1): string = string + "-" string = string self.pad.addstr(0, 0, string) @@ -23,20 +23,20 @@ class StatsDisplay: .format(self.player.level, self.player.current_xp, self.player.max_xp, self.player.health, self.player.maxhealth) - for i in range(self.width-len(string2)-1) : + for i in range(self.width - len(string2) - 1): string2 = string2 + " " self.pad.addstr(1, 0, string2) string3 = "Stats : STR {} INT {} CHR {} DEX {} CON {}"\ .format(self.player.strength, self.player.intelligence, self.player.charisma, self.player.dexterity, self.player.constitution) - for i in range(self.width-len(string3)-1) : + for i in range(self.width - len(string3) - 1): string3 = string3 + " " - self.pad.addstr(2, 0, string3) + self.pad.addstr(2, 0, string3) def refresh(self) -> None: self.pad.clear() self.update_pad() self.pad.refresh(0, 0, self.toplefty, self.topleftx, - 2+ self.toplefty, - self.width+ self.topleftx) + 2 + self.toplefty, + self.width + self.topleftx) From 8ccf8c7b670819775b680bf83782a52fd1e82cf8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 22:59:11 +0100 Subject: [PATCH 30/56] Display main menu by default --- dungeonbattle/display/display.py | 20 +++++--------------- dungeonbattle/display/menudisplay.py | 27 ++++++++++++++------------- dungeonbattle/game.py | 7 +++++-- dungeonbattle/menus.py | 6 +++--- 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index d58b772..983099a 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -1,25 +1,15 @@ import curses from typing import Any -from .mapdisplay import MapDisplay -from .texturepack import TexturePack - class Display: - # game is a game, can't import to avoid circulary includes - def __init__(self, game: Any, screen: Any): + def __init__(self, screen: Any): self.screen = screen - self.game = game - lines = curses.LINES if screen else 4 - cols = curses.COLS * 4 // 5 if screen else 4 - self.map_display = MapDisplay(game.m, - TexturePack.get_pack( - game.settings.TEXTURE_PACK - ), - lines, cols, screen is not None) + self.rows = curses.LINES if screen else 4 + self.cols = curses.COLS * 4 // 5 if screen else 4 def refresh(self) -> None: - self.map_display.update_pad() + raise NotImplementedError def display(self, y: int, x: int) -> None: - self.map_display.display(y, x) + raise NotImplementedError diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index 803311d..c46ba6b 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -1,25 +1,26 @@ +from dungeonbattle.display.display import Display from dungeonbattle.menus import Menu from typing import Any import curses -class MenuDisplay: - def __init__(self, menu: Menu, screen: Any, height: int, width: int, +class MenuDisplay(Display): + def __init__(self, screen: Any, menu: Menu, topleftx: int, toplefty: int): - self.screen = screen - self.position = menu.position + super().__init__(screen) self.values = menu.values - self.width = width - self.height = height + self.menu = menu + self.width = self.cols + self.height = self.rows self.trueheight = len(menu.values) - self.truewidth = max(map(len, self.values)) + self.truewidth = max(len(item.value) for item in menu.values) self.topleftx = topleftx self.toplefty = toplefty # Menu values are printed in pad self.pad = curses.newpad(self.trueheight, self.truewidth + 1) - for i in range(self.trueheight - 1): - self.pad.addstr(i, 0, " " + self.values[i]) + for i in range(self.trueheight): + self.pad.addstr(i, 0, " " + self.values[i].value) # Menu box self.menubox = curses.newpad(self.height, self.width) @@ -35,16 +36,16 @@ class MenuDisplay: # set a marker in front of the selected line self.pad.addstr(position, 0, ">") - def refresh(self, position: int) -> None: - if self.height - 2 >= position - 1: + def refresh(self) -> None: + if self.height - 2 >= self.menu.position - 1: cornery = 0 - elif self.height - 2 >= self.trueheight - position: + elif self.height - 2 >= self.trueheight - self.menu.position: cornery = self.trueheight - self.height + 2 self.menubox.refresh(0, 0, self.toplefty, self.topleftx, self.height + self.toplefty, self.width + self.topleftx) - self.update_pad(position) + self.update_pad(self.menu.position) self.pad.refresh(cornery, 0, self.toplefty + 1, self.topleftx + 1, self.height - 2 + self.toplefty, self.width - 2 + self.topleftx) diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index 5445bd2..e334617 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -2,6 +2,7 @@ import sys from typing import Any from .display.display import Display +from .display.menudisplay import MenuDisplay from .entities.player import Player from .interfaces import Map from .settings import Settings @@ -38,7 +39,7 @@ class Game: self.player = Player() self.player.move(1, 6) self.m.add_entity(self.player) - self.d = Display(self, screen) + self.d = MenuDisplay(self, self.main_menu, 0, 0) @staticmethod def load_game(filename: str) -> None: @@ -49,7 +50,8 @@ class Game: while True: screen.clear() screen.refresh() - self.d.display(self.player.y, self.player.x) + # self.d.display(self.player.y, self.player.x) + self.d.refresh() key = screen.getkey() self.handle_key_pressed(self.translate_key(key)) @@ -92,3 +94,4 @@ class Game: self.state = GameMode.SETTINGS elif option == menus.MainMenuValues.EXIT: sys.exit(0) + self.d.refresh() diff --git a/dungeonbattle/menus.py b/dungeonbattle/menus.py index 82d6d4e..fa4cde8 100644 --- a/dungeonbattle/menus.py +++ b/dungeonbattle/menus.py @@ -19,9 +19,9 @@ class Menu: class MainMenuValues(Enum): - START = auto() - SETTINGS = auto() - EXIT = auto() + START = 'Jouer' + SETTINGS = 'ParamΓ¨tres' + EXIT = 'Quitter' class MainMenu(Menu): From dc3f55cff3cca2d57952e555d33878aac496d4b1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 23:03:59 +0100 Subject: [PATCH 31/56] MapDisplay extends Display --- dungeonbattle/display/display.py | 3 --- dungeonbattle/display/mapdisplay.py | 21 +++++++++++++-------- dungeonbattle/game.py | 6 +++--- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 983099a..45d2160 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -10,6 +10,3 @@ class Display: def refresh(self) -> None: raise NotImplementedError - - def display(self, y: int, x: int) -> None: - raise NotImplementedError diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 3a03290..56bc370 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -1,20 +1,22 @@ #!/usr/bin/env python import curses +from typing import Any +from dungeonbattle.display.display import Display from dungeonbattle.display.texturepack import TexturePack +from dungeonbattle.entities.player import Player from dungeonbattle.interfaces import Map -class MapDisplay: - - def __init__(self, m: Map, pack: TexturePack, height: int, width: int, - init_pad: bool = True): - self.width = width - self.height = height +class MapDisplay(Display): + def __init__(self, screen: Any, m: Map, player: Player, pack: TexturePack): + super().__init__(screen) + self.height = self.rows + self.width = self.cols self.pack = pack self.map = m - if init_pad: - self.pad = curses.newpad(m.height, m.width + 1) + self.player = player + self.pad = curses.newpad(m.height, m.width + 1) def update_pad(self) -> None: self.pad.addstr(0, 0, self.map.draw_string(self.pack)) @@ -35,3 +37,6 @@ class MapDisplay: self.pad.clear() self.update_pad() self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) + + def refresh(self) -> None: + return self.display(self.player.y, self.player.x) diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index e334617..aa91a7e 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -39,7 +39,7 @@ class Game: self.player = Player() self.player.move(1, 6) self.m.add_entity(self.player) - self.d = MenuDisplay(self, self.main_menu, 0, 0) + self.current_display = MenuDisplay(self, self.main_menu, 0, 0) @staticmethod def load_game(filename: str) -> None: @@ -51,7 +51,7 @@ class Game: screen.clear() screen.refresh() # self.d.display(self.player.y, self.player.x) - self.d.refresh() + self.current_display.refresh() key = screen.getkey() self.handle_key_pressed(self.translate_key(key)) @@ -94,4 +94,4 @@ class Game: self.state = GameMode.SETTINGS elif option == menus.MainMenuValues.EXIT: sys.exit(0) - self.d.refresh() + self.current_display.refresh() From 715fad1817ffbed0581820165708961a1f6bf2fb Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 23:05:06 +0100 Subject: [PATCH 32/56] StatsDisplay extends Display --- dungeonbattle/display/statsdisplay.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index b6f17dc..37d6f91 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -1,11 +1,14 @@ import curses +from typing import Any +from dungeonbattle.display.display import Display from dungeonbattle.entities.player import Player -class StatsDisplay: - def __init__(self, player: Player, height: int, width: int, +class StatsDisplay(Display): + def __init__(self, screen: Any, player: Player, height: int, width: int, topleftx: int, toplefty: int): + super().__init__(screen) self.width = width self.height = height self.topleftx = topleftx From 7e92086601d37c3e18cc71dc1657c03a174ca9ca Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 23:08:38 +0100 Subject: [PATCH 33/56] Start game button is working --- dungeonbattle/game.py | 10 ++++++++-- dungeonbattle/mapdisplay.py | 34 ---------------------------------- 2 files changed, 8 insertions(+), 36 deletions(-) delete mode 100644 dungeonbattle/mapdisplay.py diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index aa91a7e..262469c 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -1,8 +1,9 @@ import sys from typing import Any -from .display.display import Display +from .display.mapdisplay import MapDisplay from .display.menudisplay import MenuDisplay +from .display.texturepack import TexturePack from .entities.player import Player from .interfaces import Map from .settings import Settings @@ -39,7 +40,11 @@ class Game: self.player = Player() self.player.move(1, 6) self.m.add_entity(self.player) - self.current_display = MenuDisplay(self, self.main_menu, 0, 0) + self.menu_display = MenuDisplay(screen, self.main_menu, 0, 0) + self.map_display = MapDisplay( + screen, self.m, self.player, + TexturePack.get_pack(self.settings.TEXTURE_PACK)) + self.current_display = self.menu_display @staticmethod def load_game(filename: str) -> None: @@ -90,6 +95,7 @@ class Game: option = self.main_menu.validate() if option == menus.MainMenuValues.START: self.state = GameMode.PLAY + self.current_display = self.map_display elif option == menus.MainMenuValues.SETTINGS: self.state = GameMode.SETTINGS elif option == menus.MainMenuValues.EXIT: diff --git a/dungeonbattle/mapdisplay.py b/dungeonbattle/mapdisplay.py deleted file mode 100644 index 095b7cb..0000000 --- a/dungeonbattle/mapdisplay.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -import curses - -from dungeonbattle.entities.player import Player -from dungeonbattle.interfaces import Map - - -class MapDisplay: - def __init__(self, m: Map, player: Player, init_pad: bool = True): - self.map = m - 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()) - # TODO Not all entities should be a player - for e in self.map.entities: - self.pad.addstr(e.y, e.x, '🐿') - - def display(self, y: int, x: int) -> None: - deltay, deltax = (curses.LINES // 2) + 1, (curses.COLS // 2) + 1 - pminrow, pmincol = y - deltay, x - deltax - sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) - deltay, deltax = curses.LINES - deltay, curses.COLS - deltax - smaxrow = self.map.height - (y + deltay) + curses.LINES - 1 - smaxrow = min(smaxrow, curses.LINES - 1) - smaxcol = self.map.width - (x + deltax) + curses.COLS - 1 - smaxcol = min(smaxcol, curses.COLS - 1) - pminrow = max(0, min(self.map.height, pminrow)) - pmincol = max(0, min(self.map.width, pmincol)) - self.pad.clear() - self.update_pad() - self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) From 259bde81a474692be6bddf7fa7c7e7b9c436feed Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 23:26:54 +0100 Subject: [PATCH 34/56] Back to menu by pressing space button --- dungeonbattle/game.py | 76 ++++++++++++++++++++++++++++-------------- dungeonbattle/menus.py | 2 +- 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index 262469c..96b7c36 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -1,6 +1,7 @@ import sys from typing import Any +from .display.display import Display from .display.mapdisplay import MapDisplay from .display.menudisplay import MenuDisplay from .display.texturepack import TexturePack @@ -24,9 +25,16 @@ class KeyValues(Enum): LEFT = auto() RIGHT = auto() ENTER = auto() + SPACE = auto() class Game: + map: Map + player: Player + menu_display: MenuDisplay + map_display: MapDisplay + current_display: Display + def __init__(self) -> None: self.state = GameMode.MAINMENU self.main_menu = menus.MainMenu() @@ -36,13 +44,13 @@ class Game: def new_game(self, screen: Any) -> None: # TODO generate a new map procedurally - self.m = Map.load("example_map.txt") + self.map = Map.load("example_map.txt") self.player = Player() self.player.move(1, 6) - self.m.add_entity(self.player) + self.map.add_entity(self.player) self.menu_display = MenuDisplay(screen, self.main_menu, 0, 0) self.map_display = MapDisplay( - screen, self.m, self.player, + screen, self.map, self.player, TexturePack.get_pack(self.settings.TEXTURE_PACK)) self.current_display = self.menu_display @@ -75,29 +83,47 @@ class Game: return KeyValues.UP elif key == self.settings.KEY_ENTER: return KeyValues.ENTER + elif key == ' ': + return KeyValues.SPACE 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 - self.current_display = self.map_display - elif option == menus.MainMenuValues.SETTINGS: - self.state = GameMode.SETTINGS - elif option == menus.MainMenuValues.EXIT: - sys.exit(0) + self.handle_key_pressed_play(key) + elif self.state == GameMode.MAINMENU: + self.handle_key_pressed_main_menu(key) + elif self.state == GameMode.SETTINGS: + self.handle_key_pressed_settings(key) self.current_display.refresh() + + def handle_key_pressed_play(self, key: KeyValues) -> None: + if key == KeyValues.UP: + self.player.move_up() + elif key == KeyValues.DOWN: + self.player.move_down() + elif key == KeyValues.LEFT: + self.player.move_left() + elif key == KeyValues.RIGHT: + self.player.move_right() + elif key == KeyValues.SPACE: + self.state = GameMode.MAINMENU + self.current_display = self.menu_display + + def handle_key_pressed_main_menu(self, key: KeyValues) -> None: + 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 + self.current_display = self.map_display + elif option == menus.MainMenuValues.SETTINGS: + self.state = GameMode.SETTINGS + elif option == menus.MainMenuValues.EXIT: + sys.exit(0) + + def handle_key_pressed_settings(self, key: KeyValues) -> None: + if key == KeyValues.SPACE: + self.state = GameMode.MAINMENU + self.current_display = self.menu_display diff --git a/dungeonbattle/menus.py b/dungeonbattle/menus.py index fa4cde8..c624035 100644 --- a/dungeonbattle/menus.py +++ b/dungeonbattle/menus.py @@ -1,4 +1,4 @@ -from enum import Enum, auto +from enum import Enum from typing import Any From 65d89b7f9f78db886ec11b4ed39bdfd0561ad8c2 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 23:31:17 +0100 Subject: [PATCH 35/56] Add some comments --- dungeonbattle/game.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index 96b7c36..8d5258d 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -36,6 +36,9 @@ class Game: current_display: Display def __init__(self) -> None: + """ + Init the game. + """ self.state = GameMode.MAINMENU self.main_menu = menus.MainMenu() self.settings = Settings() @@ -43,6 +46,9 @@ class Game: self.settings.write_settings() def new_game(self, screen: Any) -> None: + """ + Create a new game on the screen. + """ # TODO generate a new map procedurally self.map = Map.load("example_map.txt") self.player = Player() @@ -60,15 +66,22 @@ class Game: raise NotImplementedError() def run(self, screen: Any) -> None: + """ + Main infinite loop. + We wait for a player action, then we do what that should be done + when the given key got pressed. + """ while True: screen.clear() screen.refresh() - # self.d.display(self.player.y, self.player.x) self.current_display.refresh() key = screen.getkey() self.handle_key_pressed(self.translate_key(key)) def translate_key(self, key: str) -> KeyValues: + """ + Translate the raw string key into an enum value that we can use. + """ if key in (self.settings.KEY_DOWN_SECONDARY, self.settings.KEY_DOWN_PRIMARY): return KeyValues.DOWN @@ -87,6 +100,10 @@ class Game: return KeyValues.SPACE def handle_key_pressed(self, key: KeyValues) -> None: + """ + Indicates what should be done when the given key is pressed, + according to the current game state. + """ if self.state == GameMode.PLAY: self.handle_key_pressed_play(key) elif self.state == GameMode.MAINMENU: @@ -96,6 +113,9 @@ class Game: self.current_display.refresh() def handle_key_pressed_play(self, key: KeyValues) -> None: + """ + In play mode, arrows or zqsd should move the main character. + """ if key == KeyValues.UP: self.player.move_up() elif key == KeyValues.DOWN: @@ -109,6 +129,9 @@ class Game: self.current_display = self.menu_display def handle_key_pressed_main_menu(self, key: KeyValues) -> None: + """ + In the main menu, we can navigate through options. + """ if key == KeyValues.DOWN: self.main_menu.go_down() if key == KeyValues.UP: @@ -124,6 +147,9 @@ class Game: sys.exit(0) def handle_key_pressed_settings(self, key: KeyValues) -> None: + """ + For now, in the settings mode, we can only go backwards. + """ if key == KeyValues.SPACE: self.state = GameMode.MAINMENU self.current_display = self.menu_display From 3486c865a1c0dcb358ab0ac4e365971128fe909d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 23:40:03 +0100 Subject: [PATCH 36/56] Use a fake pad to make tests runnable --- dungeonbattle/display/display.py | 10 +++++++++- dungeonbattle/display/mapdisplay.py | 2 +- dungeonbattle/display/menudisplay.py | 4 ++-- dungeonbattle/display/statsdisplay.py | 2 +- dungeonbattle/tests/screen.py | 14 ++++++++++++++ 5 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 dungeonbattle/tests/screen.py diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 45d2160..b64ac9c 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -1,5 +1,7 @@ import curses -from typing import Any +from typing import Any, Union + +from dungeonbattle.tests.screen import FakePad class Display: @@ -10,3 +12,9 @@ class Display: def refresh(self) -> None: raise NotImplementedError + + def newpad(self, height: int, width: int) -> Union[FakePad, Any]: + if self.screen: + return curses.newpad(height, width) + else: + return FakePad() diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 56bc370..fd2cc89 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -16,7 +16,7 @@ class MapDisplay(Display): self.pack = pack self.map = m self.player = player - self.pad = curses.newpad(m.height, m.width + 1) + self.pad = self.newpad(m.height, m.width + 1) def update_pad(self) -> None: self.pad.addstr(0, 0, self.map.draw_string(self.pack)) diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index c46ba6b..d8252f8 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -18,12 +18,12 @@ class MenuDisplay(Display): self.toplefty = toplefty # Menu values are printed in pad - self.pad = curses.newpad(self.trueheight, self.truewidth + 1) + self.pad = self.newpad(self.trueheight, self.truewidth + 1) for i in range(self.trueheight): self.pad.addstr(i, 0, " " + self.values[i].value) # Menu box - self.menubox = curses.newpad(self.height, self.width) + self.menubox = self.newpad(self.height, self.width) self.menubox.addstr(0, 0, "┏" + "━" * (self.width - 2) + "β”“") for i in range(1, self.height - 2): self.menubox.addstr(i, 0, "┃" + " " * (self.width - 2) + "┃") diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 37d6f91..131d914 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -14,7 +14,7 @@ class StatsDisplay(Display): self.topleftx = topleftx self.toplefty = toplefty self.player = player - self.pad = curses.newpad(height, width) + self.pad = self.newpad(height, width) def update_pad(self) -> None: string = "" diff --git a/dungeonbattle/tests/screen.py b/dungeonbattle/tests/screen.py new file mode 100644 index 0000000..8963ce0 --- /dev/null +++ b/dungeonbattle/tests/screen.py @@ -0,0 +1,14 @@ +class FakePad: + """ + In order to run tests, we simulate a fake curses pad that accepts functions + but does nothing with them. + """ + def addstr(self, y: int, x: int, message: str) -> None: + pass + + def refresh(self, pminrow: int, pmincol: int, sminrow: int, + smincol: int, smaxrow: int, smaxcol: int) -> None: + pass + + def clear(self): + pass From 0ab0e6a00c9fcf29612fb73538cbfdaa9136e6c3 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 8 Nov 2020 23:48:26 +0100 Subject: [PATCH 37/56] More tests --- dungeonbattle/display/display.py | 9 +++------ dungeonbattle/display/mapdisplay.py | 1 - dungeonbattle/display/menudisplay.py | 1 - dungeonbattle/display/statsdisplay.py | 1 - dungeonbattle/entities/player.py | 4 ++++ dungeonbattle/tests/game_test.py | 15 ++++++++++++++- dungeonbattle/tests/screen.py | 2 +- 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index b64ac9c..d569d9b 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -7,14 +7,11 @@ from dungeonbattle.tests.screen import FakePad class Display: def __init__(self, screen: Any): self.screen = screen - self.rows = curses.LINES if screen else 4 - self.cols = curses.COLS * 4 // 5 if screen else 4 + self.rows = curses.LINES if screen else 42 + self.cols = curses.COLS * 4 // 5 if screen else 42 def refresh(self) -> None: raise NotImplementedError def newpad(self, height: int, width: int) -> Union[FakePad, Any]: - if self.screen: - return curses.newpad(height, width) - else: - return FakePad() + return curses.newpad(height, width) if self.screen else FakePad() diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index fd2cc89..38a27a2 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -import curses from typing import Any from dungeonbattle.display.display import Display diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index d8252f8..a619199 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -1,7 +1,6 @@ from dungeonbattle.display.display import Display from dungeonbattle.menus import Menu from typing import Any -import curses class MenuDisplay(Display): diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 131d914..cc5bc74 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -1,4 +1,3 @@ -import curses from typing import Any from dungeonbattle.display.display import Display diff --git a/dungeonbattle/entities/player.py b/dungeonbattle/entities/player.py index 4556f9f..9f01d0d 100644 --- a/dungeonbattle/entities/player.py +++ b/dungeonbattle/entities/player.py @@ -4,6 +4,10 @@ from ..interfaces import FightingEntity class Player(FightingEntity): maxhealth: int = 20 strength: int = 5 + intelligence: int = 1 + charisma: int = 1 + dexterity: int = 1 + constitution: int = 1 level: int = 1 current_xp: int = 0 max_xp: int = 10 diff --git a/dungeonbattle/tests/game_test.py b/dungeonbattle/tests/game_test.py index f4c1fdf..b591a68 100644 --- a/dungeonbattle/tests/game_test.py +++ b/dungeonbattle/tests/game_test.py @@ -1,5 +1,7 @@ import unittest +from dungeonbattle.display.display import Display +from dungeonbattle.display.statsdisplay import StatsDisplay from dungeonbattle.game import Game, KeyValues, GameMode from dungeonbattle.menus import MainMenuValues @@ -14,6 +16,7 @@ class TestGame(unittest.TestCase): def test_load_game(self) -> None: self.assertRaises(NotImplementedError, Game.load_game, "game.save") + self.assertRaises(NotImplementedError, Display(None).refresh) def test_key_translation(self) -> None: """ @@ -37,6 +40,7 @@ class TestGame(unittest.TestCase): self.game.settings.KEY_RIGHT_SECONDARY), KeyValues.RIGHT) self.assertEqual(self.game.translate_key( self.game.settings.KEY_ENTER), KeyValues.ENTER) + self.assertEqual(self.game.translate_key(' '), KeyValues.SPACE) def test_key_press(self) -> None: """ @@ -54,7 +58,8 @@ class TestGame(unittest.TestCase): 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.SPACE) + self.assertEqual(self.game.state, GameMode.MAINMENU) self.game.handle_key_pressed(KeyValues.DOWN) self.assertEqual(self.game.main_menu.validate(), @@ -95,3 +100,11 @@ class TestGame(unittest.TestCase): new_y, new_x = self.game.player.y, self.game.player.x self.assertEqual(new_y, y) self.assertEqual(new_x, x - 1) + + self.game.handle_key_pressed(KeyValues.SPACE) + self.assertEqual(self.game.state, GameMode.MAINMENU) + + def test_stats_display(self) -> None: + self.game.current_display = StatsDisplay(None, self.game.player, + 42, 42, 0, 0) + self.game.current_display.refresh() diff --git a/dungeonbattle/tests/screen.py b/dungeonbattle/tests/screen.py index 8963ce0..c1aad8c 100644 --- a/dungeonbattle/tests/screen.py +++ b/dungeonbattle/tests/screen.py @@ -10,5 +10,5 @@ class FakePad: smincol: int, smaxrow: int, smaxcol: int) -> None: pass - def clear(self): + def clear(self) -> None: pass From e9c8640159838e5196e9a72ffb82706538e412f1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 9 Nov 2020 00:44:08 +0100 Subject: [PATCH 38/56] Width and height are managed dynamically: we can almost freely resize the window --- dungeonbattle/display/display.py | 30 +++++++++++++++++++++++++-- dungeonbattle/display/mapdisplay.py | 3 +-- dungeonbattle/display/menudisplay.py | 19 ++++++++++------- dungeonbattle/display/statsdisplay.py | 7 +++---- dungeonbattle/tests/game_test.py | 2 +- dungeonbattle/tests/screen.py | 3 +++ 6 files changed, 48 insertions(+), 16 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index d569d9b..7604812 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -7,11 +7,37 @@ from dungeonbattle.tests.screen import FakePad class Display: def __init__(self, screen: Any): self.screen = screen - self.rows = curses.LINES if screen else 42 - self.cols = curses.COLS * 4 // 5 if screen else 42 def refresh(self) -> None: raise NotImplementedError def newpad(self, height: int, width: int) -> Union[FakePad, Any]: return curses.newpad(height, width) if self.screen else FakePad() + + def ensure_resized(self, *pads) -> bool: + """ + If the window got resized, ensure that the pads are also resized. + """ + y, x = self.screen.getmaxyx() if self.screen else (0, 0) + for pad in pads: + pad.resize(y, x) + if self.screen and curses.is_term_resized(self.rows, self.cols): + curses.resizeterm(y, x) + return True + return False + + @property + def rows(self) -> int: + return curses.LINES if self.screen else 42 + + @property + def height(self) -> int: + return self.rows + + @property + def cols(self) -> int: + return curses.COLS if self.screen else 42 + + @property + def width(self) -> int: + return self.cols diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 38a27a2..c9fc91b 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -10,8 +10,6 @@ from dungeonbattle.interfaces import Map class MapDisplay(Display): def __init__(self, screen: Any, m: Map, player: Player, pack: TexturePack): super().__init__(screen) - self.height = self.rows - self.width = self.cols self.pack = pack self.map = m self.player = player @@ -38,4 +36,5 @@ class MapDisplay(Display): self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) def refresh(self) -> None: + self.ensure_resized(self.pad) return self.display(self.player.y, self.player.x) diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index a619199..cc7a1f8 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -9,8 +9,6 @@ class MenuDisplay(Display): super().__init__(screen) self.values = menu.values self.menu = menu - self.width = self.cols - self.height = self.rows self.trueheight = len(menu.values) self.truewidth = max(len(item.value) for item in menu.values) self.topleftx = topleftx @@ -18,33 +16,40 @@ class MenuDisplay(Display): # Menu values are printed in pad self.pad = self.newpad(self.trueheight, self.truewidth + 1) - for i in range(self.trueheight): - self.pad.addstr(i, 0, " " + self.values[i].value) # Menu box self.menubox = self.newpad(self.height, self.width) + + def update_pad(self, position: int) -> None: self.menubox.addstr(0, 0, "┏" + "━" * (self.width - 2) + "β”“") for i in range(1, self.height - 2): self.menubox.addstr(i, 0, "┃" + " " * (self.width - 2) + "┃") self.menubox.addstr(self.height - 2, 0, "β”—" + "━" * (self.width - 2) + "β”›") - def update_pad(self, position: int) -> None: + for i in range(self.trueheight): + self.pad.addstr(i, 0, " " + self.values[i].value) + for i in range(self.trueheight): self.pad.addstr(i, 0, " ") # set a marker in front of the selected line self.pad.addstr(position, 0, ">") def refresh(self) -> None: + with open("/tmp/log", "a") as f: + f.write(f"{self.width}x{self.height}\n") + + self.ensure_resized(self.menubox, self.pad) + if self.height - 2 >= self.menu.position - 1: cornery = 0 elif self.height - 2 >= self.trueheight - self.menu.position: cornery = self.trueheight - self.height + 2 + self.update_pad(self.menu.position) self.menubox.refresh(0, 0, self.toplefty, self.topleftx, self.height + self.toplefty, self.width + self.topleftx) - self.update_pad(self.menu.position) self.pad.refresh(cornery, 0, self.toplefty + 1, self.topleftx + 1, - self.height - 2 + self.toplefty, + self.height - 3 + self.toplefty, self.width - 2 + self.topleftx) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index cc5bc74..9867181 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -5,15 +5,13 @@ from dungeonbattle.entities.player import Player class StatsDisplay(Display): - def __init__(self, screen: Any, player: Player, height: int, width: int, + def __init__(self, screen: Any, player: Player, topleftx: int, toplefty: int): super().__init__(screen) - self.width = width - self.height = height self.topleftx = topleftx self.toplefty = toplefty self.player = player - self.pad = self.newpad(height, width) + self.pad = self.newpad(self.height, self.width) def update_pad(self) -> None: string = "" @@ -37,6 +35,7 @@ class StatsDisplay(Display): self.pad.addstr(2, 0, string3) def refresh(self) -> None: + self.ensure_resized(self.pad) self.pad.clear() self.update_pad() self.pad.refresh(0, 0, self.toplefty, self.topleftx, diff --git a/dungeonbattle/tests/game_test.py b/dungeonbattle/tests/game_test.py index b591a68..fea99a0 100644 --- a/dungeonbattle/tests/game_test.py +++ b/dungeonbattle/tests/game_test.py @@ -106,5 +106,5 @@ class TestGame(unittest.TestCase): def test_stats_display(self) -> None: self.game.current_display = StatsDisplay(None, self.game.player, - 42, 42, 0, 0) + 42, 42) self.game.current_display.refresh() diff --git a/dungeonbattle/tests/screen.py b/dungeonbattle/tests/screen.py index c1aad8c..b9ec851 100644 --- a/dungeonbattle/tests/screen.py +++ b/dungeonbattle/tests/screen.py @@ -12,3 +12,6 @@ class FakePad: def clear(self) -> None: pass + + def resize(self, height: int, width: int) -> None: + pass From 8d5d9d38ffc5629276a930941c9e090ce841483e Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Mon, 9 Nov 2020 01:11:13 +0100 Subject: [PATCH 39/56] cleaned the arguments --- dungeonbattle/display/mapdisplay.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index c9fc91b..6ddc6d9 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -7,12 +7,15 @@ from dungeonbattle.entities.player import Player from dungeonbattle.interfaces import Map -class MapDisplay(Display): - def __init__(self, screen: Any, m: Map, player: Player, pack: TexturePack): - super().__init__(screen) +class MapDisplay: + self.map: Map + self.player: Player + + def __init__(self, screen: Any, pack: TexturePack, height : int, width : int): + self.screen = screen + self.height = height + self.width = width self.pack = pack - self.map = m - self.player = player self.pad = self.newpad(m.height, m.width + 1) def update_pad(self) -> None: @@ -20,7 +23,9 @@ class MapDisplay(Display): for e in self.map.entities: self.pad.addstr(e.y, e.x, self.pack.PLAYER) - def display(self, y: int, x: int) -> None: + def display(self, m : Map, p : Player y: int, x: int) -> None: + self.map = m + self.player = p deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1 pminrow, pmincol = y - deltay, x - deltax sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) @@ -36,5 +41,4 @@ class MapDisplay(Display): self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) def refresh(self) -> None: - self.ensure_resized(self.pad) return self.display(self.player.y, self.player.x) From a4876bb7affc87d36ec96d9903c624452c993b23 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Mon, 9 Nov 2020 01:33:23 +0100 Subject: [PATCH 40/56] repaired display --- dungeonbattle/display/display.py | 22 ++++++-- dungeonbattle/display/mapdisplay.py | 3 +- dungeonbattle/display/menudisplay.py | 79 ++++++++++++--------------- dungeonbattle/display/statsdisplay.py | 15 +++-- dungeonbattle/game.py | 19 +++---- dungeonbattle/test_display.py | 28 ++++++++++ example_map2.txt | 17 ++++++ 7 files changed, 118 insertions(+), 65 deletions(-) create mode 100644 dungeonbattle/test_display.py create mode 100644 example_map2.txt diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 7604812..ac85211 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -2,14 +2,28 @@ import curses from typing import Any, Union from dungeonbattle.tests.screen import FakePad - +from dungeonbattle.display.mapdisplay import MapDisplay +from dungeonbattle.display.statsdisplay import StatsDisplay +from dungeonbattle.display.menudisplay import MenuDisplay +from dungeonbattle.interfaces import Map +from .entities.player import Player class Display: - def __init__(self, screen: Any): + map: Map + player: Player + + def __init__(self, screen: Any, texture: Any): self.screen = screen + self.texture = texture + self.mapdisplay = MapDisplay(self.screen, self.texture, curses.LINES * 4//5, curses.COLS) + self.statsdisplay = StatsDisplay(self.screen, curses.LINES//5, curses.COLS) - def refresh(self) -> None: - raise NotImplementedError + def refresh(self, m : Map, p : Player) -> None: + self.map = m + self.player = p + self.mapdisplay.refresh(m, p, ) + self.statsdisplay.refresh(self.player) + self.menudisplay.refresh(self.position) def newpad(self, height: int, width: int) -> Union[FakePad, Any]: return curses.newpad(height, width) if self.screen else FakePad() diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 6ddc6d9..2bb59cf 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -23,9 +23,10 @@ class MapDisplay: for e in self.map.entities: self.pad.addstr(e.y, e.x, self.pack.PLAYER) - def display(self, m : Map, p : Player y: int, x: int) -> None: + def display(self, m : Map, p : Player) -> None: self.map = m self.player = p + y, x = self.map.currenty, self.map.currentx deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1 pminrow, pmincol = y - deltay, x - deltax sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index cc7a1f8..c8c6a3c 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -1,55 +1,48 @@ -from dungeonbattle.display.display import Display from dungeonbattle.menus import Menu from typing import Any +import curses - -class MenuDisplay(Display): - def __init__(self, screen: Any, menu: Menu, - topleftx: int, toplefty: int): - super().__init__(screen) +class MenuDisplay: + position: int + + def __init__(self, menu : Menu, screen: Any, height : int, width : int, topleftx: int, toplefty: int) : + self.screen = screen self.values = menu.values - self.menu = menu + self.width = width + self.height = height self.trueheight = len(menu.values) - self.truewidth = max(len(item.value) for item in menu.values) + self.truewidth = max(map(len,self.values)) self.topleftx = topleftx self.toplefty = toplefty - # Menu values are printed in pad - self.pad = self.newpad(self.trueheight, self.truewidth + 1) + #Menu values are printed in pad + self.pad = curses.newpad(self.trueheight,self.truewidth+1) + for i in range(self.trueheight-1) : + self.pad.addstr(i,0," " + self.values[i]) - # Menu box - self.menubox = self.newpad(self.height, self.width) + #Menu box + self.menubox = curses.newpad(self.height,self.width) + self.menubox.addstr(0,0,"┏"+"━"*(self.width-2)+"β”“") + for i in range(1,self.height-2) : + self.menubox.addstr(i,0,"┃"+" "*(self.width-2)+"┃") + self.menubox.addstr(self.height-2, 0, "β”—"+"━"*(self.width-2)+"β”›") + + def update_pad(self) -> None: + for i in range(self.trueheight) : + self.pad.addstr(i,0," ") + self.pad.addstr(self.position,0,">") #set a marker on the selected line - def update_pad(self, position: int) -> None: - self.menubox.addstr(0, 0, "┏" + "━" * (self.width - 2) + "β”“") - for i in range(1, self.height - 2): - self.menubox.addstr(i, 0, "┃" + " " * (self.width - 2) + "┃") - self.menubox.addstr(self.height - 2, 0, - "β”—" + "━" * (self.width - 2) + "β”›") - - for i in range(self.trueheight): - self.pad.addstr(i, 0, " " + self.values[i].value) - - for i in range(self.trueheight): - self.pad.addstr(i, 0, " ") - # set a marker in front of the selected line - self.pad.addstr(position, 0, ">") - - def refresh(self) -> None: - with open("/tmp/log", "a") as f: - f.write(f"{self.width}x{self.height}\n") - - self.ensure_resized(self.menubox, self.pad) - - if self.height - 2 >= self.menu.position - 1: + def refresh(self, position : int) -> None: + self.position = position + if self.height-2>=position-1 : cornery = 0 - elif self.height - 2 >= self.trueheight - self.menu.position: - cornery = self.trueheight - self.height + 2 - - self.update_pad(self.menu.position) + elif self.height-2 >= self.trueheight-position : + cornery = self.trueheight-self.height+2 + self.menubox.refresh(0, 0, self.toplefty, self.topleftx, - self.height + self.toplefty, - self.width + self.topleftx) - self.pad.refresh(cornery, 0, self.toplefty + 1, self.topleftx + 1, - self.height - 3 + self.toplefty, - self.width - 2 + self.topleftx) + self.height + self.toplefty, + self.width + self.topleftx) + self.update_pad(position) + self.pad.refresh(cornery, 0, self.toplefty+1, self.topleftx+1, + self.height-2 + self.toplefty, + self.width-2 + self.topleftx) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 9867181..b990ee9 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -5,19 +5,22 @@ from dungeonbattle.entities.player import Player class StatsDisplay(Display): - def __init__(self, screen: Any, player: Player, + self.player: Player + + def __init__(self, screen: Any, height: int, width: int, topleftx: int, toplefty: int): super().__init__(screen) + self.width = width + self.height = height self.topleftx = topleftx self.toplefty = toplefty - self.player = player - self.pad = self.newpad(self.height, self.width) + + self.pad = self.newpad(height, width) def update_pad(self) -> None: string = "" for i in range(self.width - 1): string = string + "-" - string = string self.pad.addstr(0, 0, string) string2 = "Player -- LVL {} EXP {}/{} HP {}/{}"\ .format(self.player.level, self.player.current_xp, @@ -34,8 +37,8 @@ class StatsDisplay(Display): string3 = string3 + " " self.pad.addstr(2, 0, string3) - def refresh(self) -> None: - self.ensure_resized(self.pad) + def refresh(self, p : Player) -> None: + self.player = p self.pad.clear() self.update_pad() self.pad.refresh(0, 0, self.toplefty, self.topleftx, diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index 8d5258d..b67d38f 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -33,7 +33,7 @@ class Game: player: Player menu_display: MenuDisplay map_display: MapDisplay - current_display: Display + display: Display def __init__(self) -> None: """ @@ -54,11 +54,8 @@ class Game: self.player = Player() self.player.move(1, 6) self.map.add_entity(self.player) - self.menu_display = MenuDisplay(screen, self.main_menu, 0, 0) - self.map_display = MapDisplay( - screen, self.map, self.player, - TexturePack.get_pack(self.settings.TEXTURE_PACK)) - self.current_display = self.menu_display + self.display = Display(screen, TexturePack.get_pack(self.settings.TEXTURE_PACK)) +# self.menu_display = MenuDisplay(screen, self.main_menu, 0, 0) @staticmethod def load_game(filename: str) -> None: @@ -74,7 +71,7 @@ class Game: while True: screen.clear() screen.refresh() - self.current_display.refresh() + self.display.refresh() key = screen.getkey() self.handle_key_pressed(self.translate_key(key)) @@ -110,7 +107,7 @@ class Game: self.handle_key_pressed_main_menu(key) elif self.state == GameMode.SETTINGS: self.handle_key_pressed_settings(key) - self.current_display.refresh() + self.display.refresh() def handle_key_pressed_play(self, key: KeyValues) -> None: """ @@ -126,7 +123,7 @@ class Game: self.player.move_right() elif key == KeyValues.SPACE: self.state = GameMode.MAINMENU - self.current_display = self.menu_display + self.display = self.menu_display def handle_key_pressed_main_menu(self, key: KeyValues) -> None: """ @@ -140,7 +137,7 @@ class Game: option = self.main_menu.validate() if option == menus.MainMenuValues.START: self.state = GameMode.PLAY - self.current_display = self.map_display + self.display = self.map_display elif option == menus.MainMenuValues.SETTINGS: self.state = GameMode.SETTINGS elif option == menus.MainMenuValues.EXIT: @@ -152,4 +149,4 @@ class Game: """ if key == KeyValues.SPACE: self.state = GameMode.MAINMENU - self.current_display = self.menu_display + self.display = self.menu_display diff --git a/dungeonbattle/test_display.py b/dungeonbattle/test_display.py new file mode 100644 index 0000000..1225fc6 --- /dev/null +++ b/dungeonbattle/test_display.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +from dungeonbattle.interfaces import Map +from dungeonbattle.display.mapdisplay import MapDisplay +from dungeonbattle.display.statsdisplay import StatsDisplay +from dungeonbattle.settings import Settings +from dungeonbattle.display.texturepack import TexturePack +from dungeonbattle.entities.player import Player +import curses +import time + +def test(screen) : + s = Settings() + s.load_settings() + s.write_settings() + p = Player() + p.y = 1 + p.x = 6 + p.health = 15 + p.intelligence = 4 + p.charisma = 2 + p.dexterity = 3 + p.constitution = 4 + m = Map.load("example_map2.txt") + MD = MapDisplay(m, TexturePack.ASCII_PACK, curses.LINES * 4//5, curses.COLS) + MD.display(p.y,p.x) + SD = StatsDisplay(p,curses.LINES * 1//5, curses.COLS, 0, curses.LINES * 4//5) + SD.refresh() + time.sleep(6) diff --git a/example_map2.txt b/example_map2.txt new file mode 100644 index 0000000..4111fae --- /dev/null +++ b/example_map2.txt @@ -0,0 +1,17 @@ + ####### ############# + #.....# #...........# + #.....# #####...........# + #.....# #...............# + #.##### #.###...........# + #.# #.# #...........# + #.# #.# ############# + #.# #.# + #.#### #.# + #....# #.# + ####.###################.# + #.....................# ################# + #.....................# #...............# + #.....................#######...............# + #...........................................# + #.....................#######...............# + ####################### ################# From 10bbb284713dc891a27bee1d609fff94fa671e88 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Mon, 9 Nov 2020 01:39:15 +0100 Subject: [PATCH 41/56] Yet again --- dungeonbattle/display/display.py | 4 ++-- dungeonbattle/display/mapdisplay.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index ac85211..8f6ce54 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -21,9 +21,9 @@ class Display: def refresh(self, m : Map, p : Player) -> None: self.map = m self.player = p - self.mapdisplay.refresh(m, p, ) + self.mapdisplay.refresh(self.map, self.player) self.statsdisplay.refresh(self.player) - self.menudisplay.refresh(self.position) +# self.menudisplay.refresh(self.position) def newpad(self, height: int, width: int) -> Union[FakePad, Any]: return curses.newpad(height, width) if self.screen else FakePad() diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 2bb59cf..7ed35b3 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -23,10 +23,7 @@ class MapDisplay: for e in self.map.entities: self.pad.addstr(e.y, e.x, self.pack.PLAYER) - def display(self, m : Map, p : Player) -> None: - self.map = m - self.player = p - y, x = self.map.currenty, self.map.currentx + def display(self) -> None: deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1 pminrow, pmincol = y - deltay, x - deltax sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) @@ -41,5 +38,8 @@ class MapDisplay: self.update_pad() self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) - def refresh(self) -> None: - return self.display(self.player.y, self.player.x) + def refresh(self, m : Map, p : Player) -> None: + self.map = m + self.player = p + y, x = self.map.currenty, self.map.currentx + return self.display() From 91a8919a01209a07d8a0d9b694f21731b0f2c92b Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 10 Nov 2020 10:01:31 +0100 Subject: [PATCH 42/56] Added a MainMenuDisplay class and fixed errors in display --- dungeonbattle/display/ascii_art.txt | 11 +++++++++++ dungeonbattle/display/display.py | 4 ++-- dungeonbattle/display/mapdisplay.py | 10 +++++----- dungeonbattle/display/menudisplay.py | 27 ++++++++++++++++++++++++--- dungeonbattle/display/statsdisplay.py | 6 +++--- dungeonbattle/display/test.py | 17 +++++++++++++++++ 6 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 dungeonbattle/display/ascii_art.txt create mode 100644 dungeonbattle/display/test.py diff --git a/dungeonbattle/display/ascii_art.txt b/dungeonbattle/display/ascii_art.txt new file mode 100644 index 0000000..966e832 --- /dev/null +++ b/dungeonbattle/display/ascii_art.txt @@ -0,0 +1,11 @@ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆ β–ˆβ–ˆ β–ˆβ–ˆβ–“ β–ˆβ–ˆβ–€β–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–€β–ˆβ–ˆβ–ˆ β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–“ β–„β–„β–„β–„ β–„β–„β–„ β–„β–„β–„β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“β–„β–„β–„β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“ β–ˆβ–ˆβ–“ β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ +β–’β–ˆβ–ˆ β–’ β–’β–ˆβ–ˆβ–“ β–ˆβ–ˆβ–’ β–ˆβ–ˆ β–“β–ˆβ–ˆβ–’β–“β–ˆβ–ˆβ–’β–“β–ˆβ–ˆ β–’ β–ˆβ–ˆβ–’β–“β–ˆβ–ˆ β–’ β–ˆβ–ˆβ–’β–“β–ˆ β–€ β–“β–ˆβ–ˆβ–’ β–“β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–„ β–’β–ˆβ–ˆβ–ˆβ–ˆβ–„ β–“ β–ˆβ–ˆβ–’ β–“β–’β–“ β–ˆβ–ˆβ–’ β–“β–’β–“β–ˆβ–ˆβ–’ β–“β–ˆ β–€ +β–‘ β–“β–ˆβ–ˆβ–„ β–’β–ˆβ–ˆβ–’ β–ˆβ–ˆβ–‘β–“β–ˆβ–ˆ β–’β–ˆβ–ˆβ–‘β–’β–ˆβ–ˆβ–’β–“β–ˆβ–ˆ β–‘β–„β–ˆ β–’β–“β–ˆβ–ˆ β–‘β–„β–ˆ β–’β–’β–ˆβ–ˆβ–ˆ β–’β–ˆβ–ˆβ–‘ β–’β–ˆβ–ˆβ–’ β–„β–ˆβ–ˆβ–’β–ˆβ–ˆ β–€β–ˆβ–„ β–’ β–“β–ˆβ–ˆβ–‘ β–’β–‘β–’ β–“β–ˆβ–ˆβ–‘ β–’β–‘β–’β–ˆβ–ˆβ–‘ β–’β–ˆβ–ˆβ–ˆ + β–’ β–ˆβ–ˆβ–’β–‘β–ˆβ–ˆ β–ˆβ–€ β–‘β–“β–“β–ˆ β–‘β–ˆβ–ˆβ–‘β–‘β–ˆβ–ˆβ–‘β–’β–ˆβ–ˆβ–€β–€β–ˆβ–„ β–’β–ˆβ–ˆβ–€β–€β–ˆβ–„ β–’β–“β–ˆ β–„ β–’β–ˆβ–ˆβ–‘ β–’β–ˆβ–ˆβ–‘β–ˆβ–€ β–‘β–ˆβ–ˆβ–„β–„β–„β–„β–ˆβ–ˆβ–‘ β–“β–ˆβ–ˆβ–“ β–‘ β–‘ β–“β–ˆβ–ˆβ–“ β–‘ β–’β–ˆβ–ˆβ–‘ β–’β–“β–ˆ β–„ +β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–‘β–’β–ˆβ–ˆβ–ˆβ–’β–ˆβ–„ β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–“ β–‘β–ˆβ–ˆβ–‘β–‘β–ˆβ–ˆβ–“ β–’β–ˆβ–ˆβ–’β–‘β–ˆβ–ˆβ–“ β–’β–ˆβ–ˆβ–’β–‘β–’β–ˆβ–ˆβ–ˆβ–ˆβ–’β–‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’ β–‘β–“β–ˆ β–€β–ˆβ–“ β–“β–ˆ β–“β–ˆβ–ˆβ–’ β–’β–ˆβ–ˆβ–’ β–‘ β–’β–ˆβ–ˆβ–’ β–‘ β–‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–‘β–’β–ˆβ–ˆβ–ˆβ–ˆβ–’ +β–’ β–’β–“β–’ β–’ β–‘β–‘β–‘ β–’β–’β–‘ β–’ β–‘β–’β–“β–’ β–’ β–’ β–‘β–“ β–‘ β–’β–“ β–‘β–’β–“β–‘β–‘ β–’β–“ β–‘β–’β–“β–‘β–‘β–‘ β–’β–‘ β–‘β–‘ β–’β–‘β–“ β–‘ β–‘β–’β–“β–ˆβ–ˆβ–ˆβ–€β–’ β–’β–’ β–“β–’β–ˆβ–‘ β–’ β–‘β–‘ β–’ β–‘β–‘ β–‘ β–’β–‘β–“ β–‘β–‘β–‘ β–’β–‘ β–‘ +β–‘ β–‘β–’ β–‘ β–‘ β–‘ β–’β–‘ β–‘ β–‘β–‘β–’β–‘ β–‘ β–‘ β–’ β–‘ β–‘β–’ β–‘ β–’β–‘ β–‘β–’ β–‘ β–’β–‘ β–‘ β–‘ β–‘β–‘ β–‘ β–’ β–‘ β–’β–‘β–’ β–‘ β–’ β–’β–’ β–‘ β–‘ β–‘ β–‘ β–‘ β–’ β–‘ β–‘ β–‘ β–‘ +β–‘ β–‘ β–‘ β–‘ β–‘ β–‘β–‘β–‘ β–‘ β–‘ β–’ β–‘ β–‘β–‘ β–‘ β–‘β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–’ β–‘ β–‘ β–‘ β–‘ β–‘ + β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ β–‘ + β–‘ + diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 8f6ce54..333f2e9 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -15,8 +15,8 @@ class Display: def __init__(self, screen: Any, texture: Any): self.screen = screen self.texture = texture - self.mapdisplay = MapDisplay(self.screen, self.texture, curses.LINES * 4//5, curses.COLS) - self.statsdisplay = StatsDisplay(self.screen, curses.LINES//5, curses.COLS) + self.mapdisplay = MapDisplay(self.texture, curses.LINES * 4//5, curses.COLS) + self.statsdisplay = StatsDisplay(curses.LINES//5, curses.COLS, curses.LINES * 4//5, 0) def refresh(self, m : Map, p : Player) -> None: self.map = m diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 7ed35b3..b795c8a 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -1,5 +1,6 @@ #!/usr/bin/env python from typing import Any +import curses from dungeonbattle.display.display import Display from dungeonbattle.display.texturepack import TexturePack @@ -11,19 +12,18 @@ class MapDisplay: self.map: Map self.player: Player - def __init__(self, screen: Any, pack: TexturePack, height : int, width : int): - self.screen = screen + def __init__(self, pack: TexturePack, height : int, width : int): self.height = height self.width = width self.pack = pack - self.pad = self.newpad(m.height, m.width + 1) + self.pad = curses.newpad(m.height, m.width + 1) def update_pad(self) -> None: self.pad.addstr(0, 0, self.map.draw_string(self.pack)) for e in self.map.entities: self.pad.addstr(e.y, e.x, self.pack.PLAYER) - def display(self) -> None: + def display(self, y, x) -> None: deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1 pminrow, pmincol = y - deltay, x - deltax sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) @@ -42,4 +42,4 @@ class MapDisplay: self.map = m self.player = p y, x = self.map.currenty, self.map.currentx - return self.display() + self.display(y,x) diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index c8c6a3c..d61d46d 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -1,12 +1,11 @@ -from dungeonbattle.menus import Menu +from dungeonbattle.menus import Menu, MainMenu from typing import Any import curses class MenuDisplay: position: int - def __init__(self, menu : Menu, screen: Any, height : int, width : int, topleftx: int, toplefty: int) : - self.screen = screen + def __init__(self, menu : Menu, height : int, width : int, topleftx: int, toplefty: int) : self.values = menu.values self.width = width self.height = height @@ -46,3 +45,25 @@ class MenuDisplay: self.pad.refresh(cornery, 0, self.toplefty+1, self.topleftx+1, self.height-2 + self.toplefty, self.width-2 + self.topleftx) + +class MainMenuDisplay: + def __init__(self, menu : MainMenu) : + self.menu = menu + self.pad = curses.newpad(curses.LINES, curses.COLS) + + with open("ascii_art.txt", "r") as file: + title = file.read().split("\n") + + width = len(title[0]) + height = len(title) + + for i in range(len(title)) : + self.pad.addstr(4+i,curses.COLS//2-width//2-1,title[i]) + self.pad.refresh(0,0,0,0,curses.LINES,curses.COLS) + + self.menudisplay = MenuDisplay(self.menu, 15, 15, height+8, curses.COLS//2-15//2-1) + + def refresh(self, position) -> None: + self.menudisplay.refresh(position) + + diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index b990ee9..60e239b 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -1,4 +1,5 @@ from typing import Any +import curses from dungeonbattle.display.display import Display from dungeonbattle.entities.player import Player @@ -7,15 +8,14 @@ from dungeonbattle.entities.player import Player class StatsDisplay(Display): self.player: Player - def __init__(self, screen: Any, height: int, width: int, + def __init__(self, height: int, width: int, topleftx: int, toplefty: int): - super().__init__(screen) self.width = width self.height = height self.topleftx = topleftx self.toplefty = toplefty - self.pad = self.newpad(height, width) + self.pad = curses.newpad(height, width) def update_pad(self) -> None: string = "" diff --git a/dungeonbattle/display/test.py b/dungeonbattle/display/test.py new file mode 100644 index 0000000..a01ae15 --- /dev/null +++ b/dungeonbattle/display/test.py @@ -0,0 +1,17 @@ +import curses +import time + +def main(screen) : + pad = curses.newpad(curses.LINES, curses.COLS) + with open("ascii_art.txt", "r") as file: + title = file.read().split("\n") + + width = len(title[0]) + for i in range(len(title)) : + pad.addstr(4+i,curses.COLS//2-width//2-1,title[i]) + pad.refresh(0,0,0,0,curses.LINES,curses.COLS) + time.sleep(1) + + + +curses.wrapper(main) From 002de68a0a8357a7c000e2807633ea68dbb8afef Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 10 Nov 2020 10:49:11 +0100 Subject: [PATCH 43/56] Corrected bugs(some still remain) --- dungeonbattle/display/display.py | 8 ++++---- dungeonbattle/display/mapdisplay.py | 7 +++---- dungeonbattle/display/statsdisplay.py | 5 ++--- dungeonbattle/game.py | 6 ++++-- example_map2.txt | 17 ----------------- 5 files changed, 13 insertions(+), 30 deletions(-) delete mode 100644 example_map2.txt diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 333f2e9..c5f8ccc 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -6,16 +6,16 @@ from dungeonbattle.display.mapdisplay import MapDisplay from dungeonbattle.display.statsdisplay import StatsDisplay from dungeonbattle.display.menudisplay import MenuDisplay from dungeonbattle.interfaces import Map -from .entities.player import Player +from dungeonbattle.entities.player import Player class Display: - map: Map player: Player - def __init__(self, screen: Any, texture: Any): + def __init__(self, screen: Any, m: Map, texture: Any): self.screen = screen self.texture = texture - self.mapdisplay = MapDisplay(self.texture, curses.LINES * 4//5, curses.COLS) + self.map = m + self.mapdisplay = MapDisplay(self.map, self.texture, curses.LINES * 4//5, curses.COLS) self.statsdisplay = StatsDisplay(curses.LINES//5, curses.COLS, curses.LINES * 4//5, 0) def refresh(self, m : Map, p : Player) -> None: diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index b795c8a..1a77f7d 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -2,20 +2,19 @@ from typing import Any import curses -from dungeonbattle.display.display import Display from dungeonbattle.display.texturepack import TexturePack from dungeonbattle.entities.player import Player from dungeonbattle.interfaces import Map class MapDisplay: - self.map: Map - self.player: Player + player: Player - def __init__(self, pack: TexturePack, height : int, width : int): + def __init__(self, m: Map, pack: TexturePack, height : int, width : int): self.height = height self.width = width self.pack = pack + self.map = m self.pad = curses.newpad(m.height, m.width + 1) def update_pad(self) -> None: diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 60e239b..26d0315 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -1,12 +1,11 @@ from typing import Any import curses -from dungeonbattle.display.display import Display from dungeonbattle.entities.player import Player -class StatsDisplay(Display): - self.player: Player +class StatsDisplay(): + player: Player def __init__(self, height: int, width: int, topleftx: int, toplefty: int): diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index b67d38f..3af8e63 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -51,10 +51,12 @@ class Game: """ # TODO generate a new map procedurally self.map = Map.load("example_map.txt") + self.map.currenty = 1 + self.map.currentx = 6 self.player = Player() self.player.move(1, 6) self.map.add_entity(self.player) - self.display = Display(screen, TexturePack.get_pack(self.settings.TEXTURE_PACK)) + self.display = Display(screen, self.map, TexturePack.get_pack(self.settings.TEXTURE_PACK)) # self.menu_display = MenuDisplay(screen, self.main_menu, 0, 0) @staticmethod @@ -71,7 +73,7 @@ class Game: while True: screen.clear() screen.refresh() - self.display.refresh() + self.display.refresh(self.map, self.player) key = screen.getkey() self.handle_key_pressed(self.translate_key(key)) diff --git a/example_map2.txt b/example_map2.txt deleted file mode 100644 index 4111fae..0000000 --- a/example_map2.txt +++ /dev/null @@ -1,17 +0,0 @@ - ####### ############# - #.....# #...........# - #.....# #####...........# - #.....# #...............# - #.##### #.###...........# - #.# #.# #...........# - #.# #.# ############# - #.# #.# - #.#### #.# - #....# #.# - ####.###################.# - #.....................# ################# - #.....................# #...............# - #.....................#######...............# - #...........................................# - #.....................#######...............# - ####################### ################# From cb33d97cb770e42669c2363b287e928a77a38919 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 10 Nov 2020 10:57:27 +0100 Subject: [PATCH 44/56] Fixed game <-> display interaction (the game prints correctly \o/ ) --- dungeonbattle/display/statsdisplay.py | 2 +- dungeonbattle/game.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 26d0315..a107749 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -8,7 +8,7 @@ class StatsDisplay(): player: Player def __init__(self, height: int, width: int, - topleftx: int, toplefty: int): + toplefty: int, topleftx: int): self.width = width self.height = height self.topleftx = topleftx diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index 3af8e63..8529431 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -109,7 +109,7 @@ class Game: self.handle_key_pressed_main_menu(key) elif self.state == GameMode.SETTINGS: self.handle_key_pressed_settings(key) - self.display.refresh() + self.display.refresh(self.map, self.player) def handle_key_pressed_play(self, key: KeyValues) -> None: """ From 2e7c12b770a1ac222364312820675f5dc5a849e7 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 10 Nov 2020 11:22:09 +0100 Subject: [PATCH 45/56] Trying to repair the test --- dungeonbattle/display/display.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index c5f8ccc..cf1d8fc 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -15,8 +15,8 @@ class Display: self.screen = screen self.texture = texture self.map = m - self.mapdisplay = MapDisplay(self.map, self.texture, curses.LINES * 4//5, curses.COLS) - self.statsdisplay = StatsDisplay(curses.LINES//5, curses.COLS, curses.LINES * 4//5, 0) + self.mapdisplay = MapDisplay(self.map, self.texture, self.rows() * 4//5, self.cols()) + self.statsdisplay = StatsDisplay(self.rows()//5, self.cols(), self.rows() * 4//5, 0) def refresh(self, m : Map, p : Player) -> None: self.map = m From e522047c7424b74f944e6e7b0b64a7089eed7e71 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 10 Nov 2020 11:39:32 +0100 Subject: [PATCH 46/56] Now the tests should trully be repaired --- dungeonbattle/display/display.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index cf1d8fc..b6c9081 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -15,8 +15,8 @@ class Display: self.screen = screen self.texture = texture self.map = m - self.mapdisplay = MapDisplay(self.map, self.texture, self.rows() * 4//5, self.cols()) - self.statsdisplay = StatsDisplay(self.rows()//5, self.cols(), self.rows() * 4//5, 0) + self.mapdisplay = MapDisplay(self.map, self.texture, self.rows * 4//5, self.cols) + self.statsdisplay = StatsDisplay(self.rows//5, self.cols, self.rows * 4//5, 0) def refresh(self, m : Map, p : Player) -> None: self.map = m From 17530f386c6adc653ce31768c8148f5d20af6b5e Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Tue, 10 Nov 2020 18:08:06 +0100 Subject: [PATCH 47/56] Reworked graphics to make it more modular --- dungeonbattle/bootstrap.py | 14 ++++ dungeonbattle/display/display.py | 55 +++++----------- dungeonbattle/display/display_manager.py | 48 ++++++++++++++ dungeonbattle/display/mapdisplay.py | 23 +++---- dungeonbattle/display/menudisplay.py | 83 ++++++++++++------------ dungeonbattle/display/statsdisplay.py | 32 +++++---- dungeonbattle/display/texturepack.py | 2 +- dungeonbattle/game.py | 22 ++----- main.py | 8 +-- 9 files changed, 155 insertions(+), 132 deletions(-) create mode 100644 dungeonbattle/bootstrap.py create mode 100644 dungeonbattle/display/display_manager.py diff --git a/dungeonbattle/bootstrap.py b/dungeonbattle/bootstrap.py new file mode 100644 index 0000000..2c241c7 --- /dev/null +++ b/dungeonbattle/bootstrap.py @@ -0,0 +1,14 @@ +from dungeonbattle.game import Game +from dungeonbattle.display.display_manager import DisplayManager +from dungeonbattle.term_manager import TermManager + +class Bootstrap: + + @staticmethod + def run_game(): + with TermManager() as term_manager: + game = Game() + game.new_game() + display = DisplayManager(term_manager.screen, game) + game.display_refresh = display.refresh + game.run(term_manager.screen) \ No newline at end of file diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index b6c9081..5633578 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -1,57 +1,36 @@ import curses from typing import Any, Union +from typing import Any from dungeonbattle.tests.screen import FakePad -from dungeonbattle.display.mapdisplay import MapDisplay -from dungeonbattle.display.statsdisplay import StatsDisplay -from dungeonbattle.display.menudisplay import MenuDisplay -from dungeonbattle.interfaces import Map -from dungeonbattle.entities.player import Player + class Display: - player: Player - - def __init__(self, screen: Any, m: Map, texture: Any): + def __init__(self, screen: Any, pack: Any): self.screen = screen - self.texture = texture - self.map = m - self.mapdisplay = MapDisplay(self.map, self.texture, self.rows * 4//5, self.cols) - self.statsdisplay = StatsDisplay(self.rows//5, self.cols, self.rows * 4//5, 0) - - def refresh(self, m : Map, p : Player) -> None: - self.map = m - self.player = p - self.mapdisplay.refresh(self.map, self.player) - self.statsdisplay.refresh(self.player) -# self.menudisplay.refresh(self.position) + self.pack = pack def newpad(self, height: int, width: int) -> Union[FakePad, Any]: return curses.newpad(height, width) if self.screen else FakePad() - def ensure_resized(self, *pads) -> bool: - """ - If the window got resized, ensure that the pads are also resized. - """ - y, x = self.screen.getmaxyx() if self.screen else (0, 0) - for pad in pads: - pad.resize(y, x) - if self.screen and curses.is_term_resized(self.rows, self.cols): - curses.resizeterm(y, x) - return True - return False + def resize(self, y, x, height, width): + self.x = x + self.y = y + self.width = width + self.height = height + + def refresh(self, *args): + if len(args) == 4: + self.resize(*args) + self.display() + + def display(self): + pass @property def rows(self) -> int: return curses.LINES if self.screen else 42 - @property - def height(self) -> int: - return self.rows - @property def cols(self) -> int: return curses.COLS if self.screen else 42 - - @property - def width(self) -> int: - return self.cols diff --git a/dungeonbattle/display/display_manager.py b/dungeonbattle/display/display_manager.py new file mode 100644 index 0000000..cf3dae3 --- /dev/null +++ b/dungeonbattle/display/display_manager.py @@ -0,0 +1,48 @@ +import curses +from dungeonbattle.display.mapdisplay import MapDisplay +from dungeonbattle.display.statsdisplay import StatsDisplay +from dungeonbattle.display.texturepack import TexturePack +from typing import Any +from dungeonbattle.game import Game + + +class DisplayManager: + + def __init__(self, screen: Any, g: Game): + self.game = g + self.screen = screen + self.mapdisplay = MapDisplay(screen, self.game.settings.TEXTURE_PACK) + self.statsdisplay = StatsDisplay(screen, self.game.settings.TEXTURE_PACK) + self.displays = [self.statsdisplay, self.mapdisplay] + self.update_game_components() + + def update_game_components(self): + for d in self.displays: + d.pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) + self.mapdisplay.update_map(self.game.map) + self.statsdisplay.update_player(self.game.player) + + def refresh(self) -> None: + self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.cols) + self.statsdisplay.refresh(self.rows*4//5, 0, self.rows//5, self.cols) +# self.menudisplay.refresh(self.position) + + def ensure_resized(self, *pads) -> bool: + """ + If the window got resized, ensure that the pads are also resized. + """ + y, x = self.screen.getmaxyx() if self.screen else (0, 0) + for pad in pads: + pad.resize(y, x) + if self.screen and curses.is_term_resized(self.rows, self.cols): + curses.resizeterm(y, x) + return True + return False + + @property + def rows(self) -> int: + return curses.LINES if self.screen else 42 + + @property + def cols(self) -> int: + return curses.COLS if self.screen else 42 \ No newline at end of file diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 1a77f7d..6438d36 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -5,15 +5,15 @@ import curses from dungeonbattle.display.texturepack import TexturePack from dungeonbattle.entities.player import Player from dungeonbattle.interfaces import Map +from .display import Display - -class MapDisplay: +class MapDisplay(Display): player: Player - def __init__(self, m: Map, pack: TexturePack, height : int, width : int): - self.height = height - self.width = width - self.pack = pack + def __init__(self, *args): + super().__init__(*args) + + def update_map(self, m: Map): self.map = m self.pad = curses.newpad(m.height, m.width + 1) @@ -22,7 +22,8 @@ class MapDisplay: for e in self.map.entities: self.pad.addstr(e.y, e.x, self.pack.PLAYER) - def display(self, y, x) -> None: + def display(self) -> None: + y, x = self.map.currenty, self.map.currentx deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1 pminrow, pmincol = y - deltay, x - deltax sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) @@ -35,10 +36,4 @@ class MapDisplay: pmincol = max(0, min(self.map.width, pmincol)) self.pad.clear() self.update_pad() - self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) - - def refresh(self, m : Map, p : Player) -> None: - self.map = m - self.player = p - y, x = self.map.currenty, self.map.currentx - self.display(y,x) + self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) \ No newline at end of file diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index d61d46d..64c41a5 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -1,69 +1,70 @@ from dungeonbattle.menus import Menu, MainMenu from typing import Any +from .display import Display import curses -class MenuDisplay: +class MenuDisplay(Display): position: int - def __init__(self, menu : Menu, height : int, width : int, topleftx: int, toplefty: int) : + def __init__(self, *args) : + super().__init__(*args) + self.menubox = self.newpad(self.height,self.width) + + def update_menu(self, menu: Menu): + self.menu = menu self.values = menu.values - self.width = width - self.height = height self.trueheight = len(menu.values) self.truewidth = max(map(len,self.values)) - self.topleftx = topleftx - self.toplefty = toplefty #Menu values are printed in pad - self.pad = curses.newpad(self.trueheight,self.truewidth+1) + self.pad = self.newpad(self.trueheight,self.truewidth+1) for i in range(self.trueheight-1) : self.pad.addstr(i,0," " + self.values[i]) + + + def update_pad(self) -> None: + for i in range(self.trueheight) : + self.pad.addstr(i,0," ") + self.pad.addstr(self.menu.position,0,">") #set a marker on the selected line + + def display(self) -> None: + if self.height-2>=self.menu.position-1 : + cornery = 0 + elif self.height-2 >= self.trueheight-self.menu.position : + cornery = self.trueheight-self.height+2 + #Menu box - self.menubox = curses.newpad(self.height,self.width) self.menubox.addstr(0,0,"┏"+"━"*(self.width-2)+"β”“") for i in range(1,self.height-2) : self.menubox.addstr(i,0,"┃"+" "*(self.width-2)+"┃") self.menubox.addstr(self.height-2, 0, "β”—"+"━"*(self.width-2)+"β”›") - def update_pad(self) -> None: - for i in range(self.trueheight) : - self.pad.addstr(i,0," ") - self.pad.addstr(self.position,0,">") #set a marker on the selected line + self.menubox.refresh(0, 0, self.y, self.x, + self.height + self.y, + self.width + self.x) + self.update_pad() + self.pad.refresh(cornery, 0, self.y+1, self.x+1, + self.height-2 + self.y, + self.width-2 + self.x) - def refresh(self, position : int) -> None: - self.position = position - if self.height-2>=position-1 : - cornery = 0 - elif self.height-2 >= self.trueheight-position : - cornery = self.trueheight-self.height+2 - - self.menubox.refresh(0, 0, self.toplefty, self.topleftx, - self.height + self.toplefty, - self.width + self.topleftx) - self.update_pad(position) - self.pad.refresh(cornery, 0, self.toplefty+1, self.topleftx+1, - self.height-2 + self.toplefty, - self.width-2 + self.topleftx) - -class MainMenuDisplay: - def __init__(self, menu : MainMenu) : +class MainMenuDisplay(Display): + def __init__(self, menu : MainMenu, *args) : + super().__init__(*args) self.menu = menu - self.pad = curses.newpad(curses.LINES, curses.COLS) + self.pad = self.newpad(self.rows, self.cols) with open("ascii_art.txt", "r") as file: - title = file.read().split("\n") + self.title = file.read().split("\n") - width = len(title[0]) - height = len(title) - - for i in range(len(title)) : - self.pad.addstr(4+i,curses.COLS//2-width//2-1,title[i]) - self.pad.refresh(0,0,0,0,curses.LINES,curses.COLS) - - self.menudisplay = MenuDisplay(self.menu, 15, 15, height+8, curses.COLS//2-15//2-1) + self.menudisplay = MenuDisplay(self.screen) + self.menudisplay.update_menu(self.menu) - def refresh(self, position) -> None: - self.menudisplay.refresh(position) + def dislpay(self) -> None: + for i in range(len(self.title)) : + self.pad.addstr(4+i,self.width//2-len(self.title[0])//2-1,self.title[i]) + self.pad.refresh(0,0,self.y,self.x,self.width,self.height) + menuy, menux = len(self.title)+8, self.width//2-15//2-1 + self.menudisplay.refresh(menuy, menux, min(15, self.rows-menuy), min(15,self.cols-menux)) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index a107749..0b15414 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -1,45 +1,43 @@ from typing import Any +from .display import Display import curses from dungeonbattle.entities.player import Player -class StatsDisplay(): +class StatsDisplay(Display): player: Player - def __init__(self, height: int, width: int, - toplefty: int, topleftx: int): - self.width = width - self.height = height - self.topleftx = topleftx - self.toplefty = toplefty - - self.pad = curses.newpad(height, width) + def __init__(self, *args): + super().__init__(*args) + self.pad = self.newpad(self.rows, self.cols) + + def update_player(self, p: Player): + self.player = p def update_pad(self) -> None: string = "" - for i in range(self.width - 1): + for _ in range(self.width - 1): string = string + "-" self.pad.addstr(0, 0, string) string2 = "Player -- LVL {} EXP {}/{} HP {}/{}"\ .format(self.player.level, self.player.current_xp, self.player.max_xp, self.player.health, self.player.maxhealth) - for i in range(self.width - len(string2) - 1): + for _ in range(self.width - len(string2) - 1): string2 = string2 + " " self.pad.addstr(1, 0, string2) string3 = "Stats : STR {} INT {} CHR {} DEX {} CON {}"\ .format(self.player.strength, self.player.intelligence, self.player.charisma, self.player.dexterity, self.player.constitution) - for i in range(self.width - len(string3) - 1): + for _ in range(self.width - len(string3) - 1): string3 = string3 + " " self.pad.addstr(2, 0, string3) - def refresh(self, p : Player) -> None: - self.player = p + def display(self) -> None: self.pad.clear() self.update_pad() - self.pad.refresh(0, 0, self.toplefty, self.topleftx, - 2 + self.toplefty, - self.width + self.topleftx) + self.pad.refresh(0, 0, self.y, self.x, + 2 + self.y, + self.width + self.x) diff --git a/dungeonbattle/display/texturepack.py b/dungeonbattle/display/texturepack.py index fa8cd2d..6929a9c 100644 --- a/dungeonbattle/display/texturepack.py +++ b/dungeonbattle/display/texturepack.py @@ -17,7 +17,7 @@ class TexturePack: @classmethod def get_pack(cls, name: str) -> "TexturePack": - return cls._packs[name] + return cls._packs[name.lower()] TexturePack.ASCII_PACK = TexturePack( diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index 8529431..b213620 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -2,14 +2,12 @@ import sys from typing import Any from .display.display import Display -from .display.mapdisplay import MapDisplay -from .display.menudisplay import MenuDisplay -from .display.texturepack import TexturePack from .entities.player import Player from .interfaces import Map from .settings import Settings from enum import Enum, auto from . import menus +from typing import Callable class GameMode(Enum): @@ -31,21 +29,19 @@ class KeyValues(Enum): class Game: map: Map player: Player - menu_display: MenuDisplay - map_display: MapDisplay - display: Display + display_refresh: Callable[[], None] def __init__(self) -> None: """ Init the game. """ - self.state = GameMode.MAINMENU + self.state = GameMode.PLAY self.main_menu = menus.MainMenu() self.settings = Settings() self.settings.load_settings() self.settings.write_settings() - def new_game(self, screen: Any) -> None: + def new_game(self) -> None: """ Create a new game on the screen. """ @@ -56,8 +52,6 @@ class Game: self.player = Player() self.player.move(1, 6) self.map.add_entity(self.player) - self.display = Display(screen, self.map, TexturePack.get_pack(self.settings.TEXTURE_PACK)) -# self.menu_display = MenuDisplay(screen, self.main_menu, 0, 0) @staticmethod def load_game(filename: str) -> None: @@ -73,9 +67,10 @@ class Game: while True: screen.clear() screen.refresh() - self.display.refresh(self.map, self.player) + self.display_refresh() key = screen.getkey() self.handle_key_pressed(self.translate_key(key)) + self.display_refresh() def translate_key(self, key: str) -> KeyValues: """ @@ -109,7 +104,7 @@ class Game: self.handle_key_pressed_main_menu(key) elif self.state == GameMode.SETTINGS: self.handle_key_pressed_settings(key) - self.display.refresh(self.map, self.player) + self.display_refresh() def handle_key_pressed_play(self, key: KeyValues) -> None: """ @@ -125,7 +120,6 @@ class Game: self.player.move_right() elif key == KeyValues.SPACE: self.state = GameMode.MAINMENU - self.display = self.menu_display def handle_key_pressed_main_menu(self, key: KeyValues) -> None: """ @@ -139,7 +133,6 @@ class Game: option = self.main_menu.validate() if option == menus.MainMenuValues.START: self.state = GameMode.PLAY - self.display = self.map_display elif option == menus.MainMenuValues.SETTINGS: self.state = GameMode.SETTINGS elif option == menus.MainMenuValues.EXIT: @@ -151,4 +144,3 @@ class Game: """ if key == KeyValues.SPACE: self.state = GameMode.MAINMENU - self.display = self.menu_display diff --git a/main.py b/main.py index 40530b9..e918f0d 100755 --- a/main.py +++ b/main.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -from dungeonbattle.game import Game -from dungeonbattle.term_manager import TermManager +from dungeonbattle.bootstrap import Bootstrap if __name__ == "__main__": - with TermManager() as term_manager: - game = Game() - game.new_game(term_manager.screen) - game.run(term_manager.screen) + Bootstrap.run_game() From 1a1b906c8eb1fd6630251c31016982a2cd10a289 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Tue, 10 Nov 2020 19:40:59 +0100 Subject: [PATCH 48/56] Added main menu when launching the game, and fixed menus --- dungeonbattle/display/display_manager.py | 19 +++++--- dungeonbattle/display/menudisplay.py | 47 +++++++++++-------- dungeonbattle/game.py | 4 +- .../display => resources}/ascii_art.txt | 0 example_map.txt => resources/example_map.txt | 0 5 files changed, 43 insertions(+), 27 deletions(-) rename {dungeonbattle/display => resources}/ascii_art.txt (100%) rename example_map.txt => resources/example_map.txt (100%) diff --git a/dungeonbattle/display/display_manager.py b/dungeonbattle/display/display_manager.py index cf3dae3..37a2876 100644 --- a/dungeonbattle/display/display_manager.py +++ b/dungeonbattle/display/display_manager.py @@ -1,9 +1,10 @@ import curses from dungeonbattle.display.mapdisplay import MapDisplay from dungeonbattle.display.statsdisplay import StatsDisplay +from dungeonbattle.display.menudisplay import MainMenuDisplay from dungeonbattle.display.texturepack import TexturePack from typing import Any -from dungeonbattle.game import Game +from dungeonbattle.game import Game, GameMode class DisplayManager: @@ -11,9 +12,11 @@ class DisplayManager: def __init__(self, screen: Any, g: Game): self.game = g self.screen = screen - self.mapdisplay = MapDisplay(screen, self.game.settings.TEXTURE_PACK) - self.statsdisplay = StatsDisplay(screen, self.game.settings.TEXTURE_PACK) - self.displays = [self.statsdisplay, self.mapdisplay] + pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) + self.mapdisplay = MapDisplay(screen, pack) + self.statsdisplay = StatsDisplay(screen, pack) + self.mainmenudisplay = MainMenuDisplay(self.game.main_menu, screen, pack) + self.displays = [self.statsdisplay, self.mapdisplay, self.mainmenudisplay] self.update_game_components() def update_game_components(self): @@ -23,8 +26,12 @@ class DisplayManager: self.statsdisplay.update_player(self.game.player) def refresh(self) -> None: - self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.cols) - self.statsdisplay.refresh(self.rows*4//5, 0, self.rows//5, self.cols) + if self.game.state == GameMode.PLAY: + self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.cols) + self.statsdisplay.refresh(self.rows*4//5, 0, self.rows//5, self.cols) + if self.game.state == GameMode.MAINMENU: + self.mainmenudisplay.refresh(0,0,self.rows, self.cols) + # self.menudisplay.refresh(self.position) def ensure_resized(self, *pads) -> bool: diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index 64c41a5..1c2cbc0 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -8,22 +8,22 @@ class MenuDisplay(Display): def __init__(self, *args) : super().__init__(*args) - self.menubox = self.newpad(self.height,self.width) + self.menubox = self.newpad(self.rows,self.cols) def update_menu(self, menu: Menu): self.menu = menu - self.values = menu.values - self.trueheight = len(menu.values) - self.truewidth = max(map(len,self.values)) + self.values = [ str(a) for a in menu.values ] + self.trueheight = len(self.values) + self.truewidth = max([len(a) for a in self.values]) #Menu values are printed in pad - self.pad = self.newpad(self.trueheight,self.truewidth+1) - for i in range(self.trueheight-1) : - self.pad.addstr(i,0," " + self.values[i]) + self.pad = self.newpad(self.trueheight,self.truewidth+2) + for i in range(self.trueheight) : + self.pad.addstr(i,0," " + self.values[i]) def update_pad(self) -> None: - for i in range(self.trueheight) : + for i in range(self.trueheight): self.pad.addstr(i,0," ") self.pad.addstr(self.menu.position,0,">") #set a marker on the selected line @@ -36,17 +36,25 @@ class MenuDisplay(Display): #Menu box self.menubox.addstr(0,0,"┏"+"━"*(self.width-2)+"β”“") - for i in range(1,self.height-2) : + for i in range(1,self.height-1) : self.menubox.addstr(i,0,"┃"+" "*(self.width-2)+"┃") - self.menubox.addstr(self.height-2, 0, "β”—"+"━"*(self.width-2)+"β”›") + self.menubox.addstr(self.height-1, 0, "β”—"+"━"*(self.width-2)+"β”›") self.menubox.refresh(0, 0, self.y, self.x, self.height + self.y, self.width + self.x) self.update_pad() - self.pad.refresh(cornery, 0, self.y+1, self.x+1, - self.height-2 + self.y, - self.width-2 + self.x) + self.pad.refresh(cornery, 0, self.y+1, self.x+2, + self.height-1 + self.y, + self.width-1 + self.x) + + @property + def preferred_width(self) -> int: + return self.truewidth + 6 + + @property + def preferred_height(self) -> int: + return self.trueheight + 2 class MainMenuDisplay(Display): def __init__(self, menu : MainMenu, *args) : @@ -54,17 +62,18 @@ class MainMenuDisplay(Display): self.menu = menu self.pad = self.newpad(self.rows, self.cols) - with open("ascii_art.txt", "r") as file: + with open("resources/ascii_art.txt", "r") as file: self.title = file.read().split("\n") - self.menudisplay = MenuDisplay(self.screen) + self.menudisplay = MenuDisplay(self.screen, self.pack) self.menudisplay.update_menu(self.menu) - def dislpay(self) -> None: + def display(self) -> None: for i in range(len(self.title)) : self.pad.addstr(4+i,self.width//2-len(self.title[0])//2-1,self.title[i]) - self.pad.refresh(0,0,self.y,self.x,self.width,self.height) - menuy, menux = len(self.title)+8, self.width//2-15//2-1 - self.menudisplay.refresh(menuy, menux, min(15, self.rows-menuy), min(15,self.cols-menux)) + self.pad.refresh(0,0,self.y,self.x,self.height,self.width) + menuwidth = min(self.menudisplay.preferred_width, self.width) + menuy, menux = len(self.title)+8, self.width//2-menuwidth//2-1 + self.menudisplay.refresh(menuy, menux, min(self.menudisplay.preferred_height, self.height-menuy), menuwidth) diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index b213620..9ed09ce 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -35,7 +35,7 @@ class Game: """ Init the game. """ - self.state = GameMode.PLAY + self.state = GameMode.MAINMENU self.main_menu = menus.MainMenu() self.settings = Settings() self.settings.load_settings() @@ -46,7 +46,7 @@ class Game: Create a new game on the screen. """ # TODO generate a new map procedurally - self.map = Map.load("example_map.txt") + self.map = Map.load("resources/example_map.txt") self.map.currenty = 1 self.map.currentx = 6 self.player = Player() diff --git a/dungeonbattle/display/ascii_art.txt b/resources/ascii_art.txt similarity index 100% rename from dungeonbattle/display/ascii_art.txt rename to resources/ascii_art.txt diff --git a/example_map.txt b/resources/example_map.txt similarity index 100% rename from example_map.txt rename to resources/example_map.txt From 1ff2e26cd43bf96b0a9ad5af7a627d9660ad25c8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Nov 2020 20:22:53 +0100 Subject: [PATCH 49/56] Set good menu names --- dungeonbattle/menus.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dungeonbattle/menus.py b/dungeonbattle/menus.py index c624035..68eeab7 100644 --- a/dungeonbattle/menus.py +++ b/dungeonbattle/menus.py @@ -23,6 +23,9 @@ class MainMenuValues(Enum): SETTINGS = 'ParamΓ¨tres' EXIT = 'Quitter' + def __str__(self): + return self.value + class MainMenu(Menu): values = [e for e in MainMenuValues] From 5c95bf11e7b0d71d8a89a237300e018f3a5f77fe Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Nov 2020 20:34:22 +0100 Subject: [PATCH 50/56] Linting --- dungeonbattle/bootstrap.py | 3 +- dungeonbattle/display/display.py | 16 +++-- dungeonbattle/display/display_manager.py | 19 +++--- dungeonbattle/display/mapdisplay.py | 13 ++-- dungeonbattle/display/menudisplay.py | 83 ++++++++++++------------ dungeonbattle/display/statsdisplay.py | 8 +-- dungeonbattle/game.py | 1 - 7 files changed, 72 insertions(+), 71 deletions(-) diff --git a/dungeonbattle/bootstrap.py b/dungeonbattle/bootstrap.py index 2c241c7..4c2c851 100644 --- a/dungeonbattle/bootstrap.py +++ b/dungeonbattle/bootstrap.py @@ -2,6 +2,7 @@ from dungeonbattle.game import Game from dungeonbattle.display.display_manager import DisplayManager from dungeonbattle.term_manager import TermManager + class Bootstrap: @staticmethod @@ -11,4 +12,4 @@ class Bootstrap: game.new_game() display = DisplayManager(term_manager.screen, game) game.display_refresh = display.refresh - game.run(term_manager.screen) \ No newline at end of file + game.run(term_manager.screen) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 5633578..2e15a26 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -1,11 +1,15 @@ import curses from typing import Any, Union -from typing import Any from dungeonbattle.tests.screen import FakePad class Display: + x: int + y: int + width: int + height: int + def __init__(self, screen: Any, pack: Any): self.screen = screen self.pack = pack @@ -13,19 +17,19 @@ class Display: def newpad(self, height: int, width: int) -> Union[FakePad, Any]: return curses.newpad(height, width) if self.screen else FakePad() - def resize(self, y, x, height, width): + def resize(self, y: int, x: int, height: int, width: int) -> None: self.x = x self.y = y self.width = width self.height = height - def refresh(self, *args): + def refresh(self, *args) -> None: if len(args) == 4: self.resize(*args) self.display() - - def display(self): - pass + + def display(self) -> None: + raise NotImplementedError @property def rows(self) -> int: diff --git a/dungeonbattle/display/display_manager.py b/dungeonbattle/display/display_manager.py index 37a2876..c4d3768 100644 --- a/dungeonbattle/display/display_manager.py +++ b/dungeonbattle/display/display_manager.py @@ -8,18 +8,20 @@ from dungeonbattle.game import Game, GameMode class DisplayManager: - + def __init__(self, screen: Any, g: Game): self.game = g self.screen = screen pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) self.mapdisplay = MapDisplay(screen, pack) self.statsdisplay = StatsDisplay(screen, pack) - self.mainmenudisplay = MainMenuDisplay(self.game.main_menu, screen, pack) - self.displays = [self.statsdisplay, self.mapdisplay, self.mainmenudisplay] + self.mainmenudisplay = MainMenuDisplay(self.game.main_menu, + screen, pack) + self.displays = [self.statsdisplay, self.mapdisplay, + self.mainmenudisplay] self.update_game_components() - def update_game_components(self): + def update_game_components(self) -> None: for d in self.displays: d.pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) self.mapdisplay.update_map(self.game.map) @@ -28,11 +30,10 @@ class DisplayManager: def refresh(self) -> None: if self.game.state == GameMode.PLAY: self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.cols) - self.statsdisplay.refresh(self.rows*4//5, 0, self.rows//5, self.cols) + self.statsdisplay.refresh(self.rows * 4 // 5, 0, + self.rows // 5, self.cols) if self.game.state == GameMode.MAINMENU: - self.mainmenudisplay.refresh(0,0,self.rows, self.cols) - -# self.menudisplay.refresh(self.position) + self.mainmenudisplay.refresh(0, 0, self.rows, self.cols) def ensure_resized(self, *pads) -> bool: """ @@ -52,4 +53,4 @@ class DisplayManager: @property def cols(self) -> int: - return curses.COLS if self.screen else 42 \ No newline at end of file + return curses.COLS if self.screen else 42 diff --git a/dungeonbattle/display/mapdisplay.py b/dungeonbattle/display/mapdisplay.py index 6438d36..0a07ec0 100644 --- a/dungeonbattle/display/mapdisplay.py +++ b/dungeonbattle/display/mapdisplay.py @@ -1,21 +1,18 @@ #!/usr/bin/env python -from typing import Any -import curses - -from dungeonbattle.display.texturepack import TexturePack from dungeonbattle.entities.player import Player from dungeonbattle.interfaces import Map from .display import Display + class MapDisplay(Display): player: Player - + def __init__(self, *args): super().__init__(*args) - def update_map(self, m: Map): + def update_map(self, m: Map) -> None: self.map = m - self.pad = curses.newpad(m.height, m.width + 1) + self.pad = self.newpad(m.height, m.width + 1) def update_pad(self) -> None: self.pad.addstr(0, 0, self.map.draw_string(self.pack)) @@ -36,4 +33,4 @@ class MapDisplay(Display): pmincol = max(0, min(self.map.width, pmincol)) self.pad.clear() self.update_pad() - self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) \ No newline at end of file + self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol) diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index 1c2cbc0..6bcf9fc 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -1,63 +1,63 @@ from dungeonbattle.menus import Menu, MainMenu -from typing import Any from .display import Display -import curses + class MenuDisplay(Display): position: int - - def __init__(self, *args) : - super().__init__(*args) - self.menubox = self.newpad(self.rows,self.cols) - def update_menu(self, menu: Menu): + def __init__(self, *args): + super().__init__(*args) + self.menubox = self.newpad(self.rows, self.cols) + + def update_menu(self, menu: Menu) -> None: self.menu = menu - self.values = [ str(a) for a in menu.values ] + self.values = [str(a) for a in menu.values] self.trueheight = len(self.values) self.truewidth = max([len(a) for a in self.values]) - #Menu values are printed in pad - self.pad = self.newpad(self.trueheight,self.truewidth+2) - for i in range(self.trueheight) : - self.pad.addstr(i,0," " + self.values[i]) - + # Menu values are printed in pad + self.pad = self.newpad(self.trueheight, self.truewidth + 2) + for i in range(self.trueheight): + self.pad.addstr(i, 0, " " + self.values[i]) def update_pad(self) -> None: for i in range(self.trueheight): - self.pad.addstr(i,0," ") - self.pad.addstr(self.menu.position,0,">") #set a marker on the selected line + self.pad.addstr(i, 0, " ") + # set a marker on the selected line + self.pad.addstr(self.menu.position, 0, ">") def display(self) -> None: - if self.height-2>=self.menu.position-1 : + if self.height - 2 >= self.menu.position - 1: cornery = 0 - elif self.height-2 >= self.trueheight-self.menu.position : - cornery = self.trueheight-self.height+2 - + elif self.height - 2 >= self.trueheight - self.menu.position: + cornery = self.trueheight - self.height + 2 + + # Menu box + self.menubox.addstr(0, 0, "┏" + "━" * (self.width - 2) + "β”“") + for i in range(1, self.height - 1): + self.menubox.addstr(i, 0, "┃" + " " * (self.width - 2) + "┃") + self.menubox.addstr(self.height - 1, 0, + "β”—" + "━" * (self.width - 2) + "β”›") - #Menu box - self.menubox.addstr(0,0,"┏"+"━"*(self.width-2)+"β”“") - for i in range(1,self.height-1) : - self.menubox.addstr(i,0,"┃"+" "*(self.width-2)+"┃") - self.menubox.addstr(self.height-1, 0, "β”—"+"━"*(self.width-2)+"β”›") - self.menubox.refresh(0, 0, self.y, self.x, - self.height + self.y, - self.width + self.x) + self.height + self.y, + self.width + self.x) self.update_pad() - self.pad.refresh(cornery, 0, self.y+1, self.x+2, - self.height-1 + self.y, - self.width-1 + self.x) - + self.pad.refresh(cornery, 0, self.y + 1, self.x + 2, + self.height - 1 + self.y, + self.width - 1 + self.x) + @property def preferred_width(self) -> int: return self.truewidth + 6 - + @property def preferred_height(self) -> int: return self.trueheight + 2 + class MainMenuDisplay(Display): - def __init__(self, menu : MainMenu, *args) : + def __init__(self, menu: MainMenu, *args): super().__init__(*args) self.menu = menu self.pad = self.newpad(self.rows, self.cols) @@ -67,13 +67,14 @@ class MainMenuDisplay(Display): self.menudisplay = MenuDisplay(self.screen, self.pack) self.menudisplay.update_menu(self.menu) - + def display(self) -> None: - for i in range(len(self.title)) : - self.pad.addstr(4+i,self.width//2-len(self.title[0])//2-1,self.title[i]) - self.pad.refresh(0,0,self.y,self.x,self.height,self.width) + for i in range(len(self.title)): + self.pad.addstr(4 + i, self.width // 2 + - len(self.title[0]) // 2 - 1, self.title[i]) + self.pad.refresh(0, 0, self.y, self.x, self.height, self.width) menuwidth = min(self.menudisplay.preferred_width, self.width) - menuy, menux = len(self.title)+8, self.width//2-menuwidth//2-1 - self.menudisplay.refresh(menuy, menux, min(self.menudisplay.preferred_height, self.height-menuy), menuwidth) - - + menuy, menux = len(self.title) + 8, self.width // 2 - menuwidth // 2 - 1 + self.menudisplay.refresh( + menuy, menux, min(self.menudisplay.preferred_height, + self.height - menuy), menuwidth) diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 0b15414..6ec27bc 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -1,18 +1,16 @@ -from typing import Any from .display import Display -import curses from dungeonbattle.entities.player import Player class StatsDisplay(Display): player: Player - + def __init__(self, *args): super().__init__(*args) self.pad = self.newpad(self.rows, self.cols) - - def update_player(self, p: Player): + + def update_player(self, p: Player) -> None: self.player = p def update_pad(self) -> None: diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index 9ed09ce..f0989c9 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -1,7 +1,6 @@ import sys from typing import Any -from .display.display import Display from .entities.player import Player from .interfaces import Map from .settings import Settings From 9b925672cac9a63f76a39da0810100a2911d1093 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Nov 2020 20:36:09 +0100 Subject: [PATCH 51/56] Drop some tests --- dungeonbattle/display/test.py | 17 ----------------- dungeonbattle/test_display.py | 28 ---------------------------- 2 files changed, 45 deletions(-) delete mode 100644 dungeonbattle/display/test.py delete mode 100644 dungeonbattle/test_display.py diff --git a/dungeonbattle/display/test.py b/dungeonbattle/display/test.py deleted file mode 100644 index a01ae15..0000000 --- a/dungeonbattle/display/test.py +++ /dev/null @@ -1,17 +0,0 @@ -import curses -import time - -def main(screen) : - pad = curses.newpad(curses.LINES, curses.COLS) - with open("ascii_art.txt", "r") as file: - title = file.read().split("\n") - - width = len(title[0]) - for i in range(len(title)) : - pad.addstr(4+i,curses.COLS//2-width//2-1,title[i]) - pad.refresh(0,0,0,0,curses.LINES,curses.COLS) - time.sleep(1) - - - -curses.wrapper(main) diff --git a/dungeonbattle/test_display.py b/dungeonbattle/test_display.py deleted file mode 100644 index 1225fc6..0000000 --- a/dungeonbattle/test_display.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -from dungeonbattle.interfaces import Map -from dungeonbattle.display.mapdisplay import MapDisplay -from dungeonbattle.display.statsdisplay import StatsDisplay -from dungeonbattle.settings import Settings -from dungeonbattle.display.texturepack import TexturePack -from dungeonbattle.entities.player import Player -import curses -import time - -def test(screen) : - s = Settings() - s.load_settings() - s.write_settings() - p = Player() - p.y = 1 - p.x = 6 - p.health = 15 - p.intelligence = 4 - p.charisma = 2 - p.dexterity = 3 - p.constitution = 4 - m = Map.load("example_map2.txt") - MD = MapDisplay(m, TexturePack.ASCII_PACK, curses.LINES * 4//5, curses.COLS) - MD.display(p.y,p.x) - SD = StatsDisplay(p,curses.LINES * 1//5, curses.COLS, 0, curses.LINES * 4//5) - SD.refresh() - time.sleep(6) From f9c3fc151735b7f0fc675001fa92c4ce9439ccd4 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Nov 2020 20:37:01 +0100 Subject: [PATCH 52/56] Example map was moved --- dungeonbattle/tests/entities_test.py | 2 +- dungeonbattle/tests/interfaces_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dungeonbattle/tests/entities_test.py b/dungeonbattle/tests/entities_test.py index e143926..00ea33b 100644 --- a/dungeonbattle/tests/entities_test.py +++ b/dungeonbattle/tests/entities_test.py @@ -11,7 +11,7 @@ class TestEntities(unittest.TestCase): """ Load example map that can be used in tests. """ - self.map = Map.load("example_map.txt") + self.map = Map.load("resources/example_map.txt") def test_basic_entities(self) -> None: """ diff --git a/dungeonbattle/tests/interfaces_test.py b/dungeonbattle/tests/interfaces_test.py index 56e0e1d..c36e895 100644 --- a/dungeonbattle/tests/interfaces_test.py +++ b/dungeonbattle/tests/interfaces_test.py @@ -18,7 +18,7 @@ class TestInterfaces(unittest.TestCase): """ Try to load a map from a file. """ - m = Map.load("example_map.txt") + m = Map.load("resources/example_map.txt") self.assertEqual(m.width, 52) self.assertEqual(m.height, 17) From d50c775e0fcf065632a6ac5ebaa52bb57b23a844 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Nov 2020 20:43:30 +0100 Subject: [PATCH 53/56] Mysteriously fix tests --- dungeonbattle/display/display.py | 7 ++++--- dungeonbattle/display/statsdisplay.py | 4 ++-- dungeonbattle/tests/game_test.py | 12 ++++++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index 2e15a26..fafcfeb 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -1,6 +1,7 @@ import curses -from typing import Any, Union +from typing import Any, Optional, Union +from dungeonbattle.display.texturepack import TexturePack from dungeonbattle.tests.screen import FakePad @@ -10,9 +11,9 @@ class Display: width: int height: int - def __init__(self, screen: Any, pack: Any): + def __init__(self, screen: Any, pack: Optional[TexturePack] = None): self.screen = screen - self.pack = pack + self.pack = pack or TexturePack.get_pack("ascii") def newpad(self, height: int, width: int) -> Union[FakePad, Any]: return curses.newpad(height, width) if self.screen else FakePad() diff --git a/dungeonbattle/display/statsdisplay.py b/dungeonbattle/display/statsdisplay.py index 6ec27bc..bf204dd 100644 --- a/dungeonbattle/display/statsdisplay.py +++ b/dungeonbattle/display/statsdisplay.py @@ -6,8 +6,8 @@ from dungeonbattle.entities.player import Player class StatsDisplay(Display): player: Player - def __init__(self, *args): - super().__init__(*args) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.pad = self.newpad(self.rows, self.cols) def update_player(self, p: Player) -> None: diff --git a/dungeonbattle/tests/game_test.py b/dungeonbattle/tests/game_test.py index fea99a0..c933847 100644 --- a/dungeonbattle/tests/game_test.py +++ b/dungeonbattle/tests/game_test.py @@ -1,6 +1,7 @@ import unittest from dungeonbattle.display.display import Display +from dungeonbattle.display.display_manager import DisplayManager from dungeonbattle.display.statsdisplay import StatsDisplay from dungeonbattle.game import Game, KeyValues, GameMode from dungeonbattle.menus import MainMenuValues @@ -12,11 +13,13 @@ class TestGame(unittest.TestCase): Setup game. """ self.game = Game() - self.game.new_game(None) + self.game.new_game() + display = DisplayManager(None, self.game) + self.game.display_refresh = display.refresh def test_load_game(self) -> None: self.assertRaises(NotImplementedError, Game.load_game, "game.save") - self.assertRaises(NotImplementedError, Display(None).refresh) + self.assertRaises(NotImplementedError, Display(None).display) def test_key_translation(self) -> None: """ @@ -105,6 +108,7 @@ class TestGame(unittest.TestCase): self.assertEqual(self.game.state, GameMode.MAINMENU) def test_stats_display(self) -> None: - self.game.current_display = StatsDisplay(None, self.game.player, - 42, 42) + self.game.current_display = StatsDisplay(None) + self.game.current_display.update_player(self.game.player) + self.game.current_display.resize(0, 0, 42, 42) self.game.current_display.refresh() From e9ac448854ab12f40b873a473959ac5a6092e663 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Nov 2020 21:20:11 +0100 Subject: [PATCH 54/56] Screen is resizable (but please be nice with it) --- dungeonbattle/display/display.py | 3 +++ dungeonbattle/display/display_manager.py | 7 +++---- dungeonbattle/display/menudisplay.py | 4 ++-- dungeonbattle/game.py | 1 - 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/dungeonbattle/display/display.py b/dungeonbattle/display/display.py index fafcfeb..64314e9 100644 --- a/dungeonbattle/display/display.py +++ b/dungeonbattle/display/display.py @@ -10,6 +10,7 @@ class Display: y: int width: int height: int + pad: Any def __init__(self, screen: Any, pack: Optional[TexturePack] = None): self.screen = screen @@ -23,6 +24,8 @@ class Display: self.y = y self.width = width self.height = height + if self.pad: + self.pad.resize(height - 1, width - 1) def refresh(self, *args) -> None: if len(args) == 4: diff --git a/dungeonbattle/display/display_manager.py b/dungeonbattle/display/display_manager.py index c4d3768..2564579 100644 --- a/dungeonbattle/display/display_manager.py +++ b/dungeonbattle/display/display_manager.py @@ -34,14 +34,13 @@ class DisplayManager: self.rows // 5, self.cols) if self.game.state == GameMode.MAINMENU: self.mainmenudisplay.refresh(0, 0, self.rows, self.cols) + self.resize_window() - def ensure_resized(self, *pads) -> bool: + def resize_window(self) -> bool: """ - If the window got resized, ensure that the pads are also resized. + If the window got resized, ensure that the screen size got updated. """ y, x = self.screen.getmaxyx() if self.screen else (0, 0) - for pad in pads: - pad.resize(y, x) if self.screen and curses.is_term_resized(self.rows, self.cols): curses.resizeterm(y, x) return True diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index 6bcf9fc..257b271 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -44,8 +44,8 @@ class MenuDisplay(Display): self.width + self.x) self.update_pad() self.pad.refresh(cornery, 0, self.y + 1, self.x + 2, - self.height - 1 + self.y, - self.width - 1 + self.x) + self.height - 2 + self.y, + self.width - 2 + self.x) @property def preferred_width(self) -> int: diff --git a/dungeonbattle/game.py b/dungeonbattle/game.py index f0989c9..3dc60cc 100644 --- a/dungeonbattle/game.py +++ b/dungeonbattle/game.py @@ -69,7 +69,6 @@ class Game: self.display_refresh() key = screen.getkey() self.handle_key_pressed(self.translate_key(key)) - self.display_refresh() def translate_key(self, key: str) -> KeyValues: """ From 7173d134951cfe0348baa529dd1c16f7906e7b52 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Nov 2020 21:32:10 +0100 Subject: [PATCH 55/56] Slightly cover bootstrap, to increase more and more coverage. Ensure that there is no associated shell --- dungeonbattle/display/menudisplay.py | 7 +++---- dungeonbattle/tests/game_test.py | 18 +++++++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/dungeonbattle/display/menudisplay.py b/dungeonbattle/display/menudisplay.py index 257b271..aeed447 100644 --- a/dungeonbattle/display/menudisplay.py +++ b/dungeonbattle/display/menudisplay.py @@ -27,10 +27,9 @@ class MenuDisplay(Display): self.pad.addstr(self.menu.position, 0, ">") def display(self) -> None: - if self.height - 2 >= self.menu.position - 1: - cornery = 0 - elif self.height - 2 >= self.trueheight - self.menu.position: - cornery = self.trueheight - self.height + 2 + cornery = 0 if self.height - 2 >= self.menu.position - 1 \ + else self.trueheight - self.height + 2 \ + if self.height - 2 >= self.trueheight - self.menu.position else 0 # Menu box self.menubox.addstr(0, 0, "┏" + "━" * (self.width - 2) + "β”“") diff --git a/dungeonbattle/tests/game_test.py b/dungeonbattle/tests/game_test.py index c933847..2498b48 100644 --- a/dungeonbattle/tests/game_test.py +++ b/dungeonbattle/tests/game_test.py @@ -1,8 +1,9 @@ +import os import unittest +from dungeonbattle.bootstrap import Bootstrap from dungeonbattle.display.display import Display from dungeonbattle.display.display_manager import DisplayManager -from dungeonbattle.display.statsdisplay import StatsDisplay from dungeonbattle.game import Game, KeyValues, GameMode from dungeonbattle.menus import MainMenuValues @@ -21,6 +22,15 @@ class TestGame(unittest.TestCase): self.assertRaises(NotImplementedError, Game.load_game, "game.save") self.assertRaises(NotImplementedError, Display(None).display) + def test_bootstrap_fail(self) -> None: + """ + Ensure that the test can't play the game, + because there is no associated shell. + Yeah, that's only for coverage. + """ + self.assertRaises(Exception, Bootstrap.run_game) + self.assertEqual(os.getenv("TERM", "unknown"), "unknown") + def test_key_translation(self) -> None: """ Test key bindings. @@ -106,9 +116,3 @@ class TestGame(unittest.TestCase): self.game.handle_key_pressed(KeyValues.SPACE) self.assertEqual(self.game.state, GameMode.MAINMENU) - - def test_stats_display(self) -> None: - self.game.current_display = StatsDisplay(None) - self.game.current_display.update_player(self.game.player) - self.game.current_display.resize(0, 0, 42, 42) - self.game.current_display.refresh() From 3416ce2ac3bd2dce2c1901322355296cec7a2127 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Nov 2020 21:32:42 +0100 Subject: [PATCH 56/56] Ignore unreachable code in test environment, we have now 99% of coverage --- dungeonbattle/bootstrap.py | 2 +- dungeonbattle/display/display_manager.py | 3 ++- dungeonbattle/term_manager.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dungeonbattle/bootstrap.py b/dungeonbattle/bootstrap.py index 4c2c851..a2b5c72 100644 --- a/dungeonbattle/bootstrap.py +++ b/dungeonbattle/bootstrap.py @@ -7,7 +7,7 @@ class Bootstrap: @staticmethod def run_game(): - with TermManager() as term_manager: + with TermManager() as term_manager: # pragma: no cover game = Game() game.new_game() display = DisplayManager(term_manager.screen, game) diff --git a/dungeonbattle/display/display_manager.py b/dungeonbattle/display/display_manager.py index 2564579..5c249c8 100644 --- a/dungeonbattle/display/display_manager.py +++ b/dungeonbattle/display/display_manager.py @@ -41,7 +41,8 @@ class DisplayManager: If the window got resized, ensure that the screen size got updated. """ y, x = self.screen.getmaxyx() if self.screen else (0, 0) - if self.screen and curses.is_term_resized(self.rows, self.cols): + if self.screen and curses.is_term_resized(self.rows, + self.cols): # pragma: nocover curses.resizeterm(y, x) return True return False diff --git a/dungeonbattle/term_manager.py b/dungeonbattle/term_manager.py index 1f1d364..ab7f4dd 100644 --- a/dungeonbattle/term_manager.py +++ b/dungeonbattle/term_manager.py @@ -2,7 +2,7 @@ import curses from types import TracebackType -class TermManager: +class TermManager: # pragma: no cover def __init__(self): self.screen = curses.initscr() # convert escapes sequences to curses abstraction