Reworked graphics to make it more modular

This commit is contained in:
Nicolas Margulies
2020-11-10 18:08:06 +01:00
parent e522047c74
commit 17530f386c
9 changed files with 155 additions and 132 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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(