Higher abstraction level on addmsg, fixes #43
This commit is contained in:
		@@ -2,7 +2,7 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import curses
 | 
			
		||||
from typing import Any, Optional, Union
 | 
			
		||||
from typing import Any, Optional, Tuple, Union
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.display.texturepack import TexturePack
 | 
			
		||||
from squirrelbattle.game import Game
 | 
			
		||||
@@ -16,6 +16,9 @@ class Display:
 | 
			
		||||
    height: int
 | 
			
		||||
    pad: Any
 | 
			
		||||
 | 
			
		||||
    _color_pairs = {(curses.COLOR_WHITE, curses.COLOR_BLACK): 0}
 | 
			
		||||
    _colors_rgb = {}
 | 
			
		||||
 | 
			
		||||
    def __init__(self, screen: Any, pack: Optional[TexturePack] = None):
 | 
			
		||||
        self.screen = screen
 | 
			
		||||
        self.pack = pack or TexturePack.get_pack("ascii")
 | 
			
		||||
@@ -31,15 +34,84 @@ class Display:
 | 
			
		||||
        lines = [line[:width] for line in lines]
 | 
			
		||||
        return "\n".join(lines)
 | 
			
		||||
 | 
			
		||||
    def addstr(self, pad: Any, y: int, x: int, msg: str, *options) -> None:
 | 
			
		||||
    def translate_color(self, color: Union[int, Tuple[int, int, int]]) -> int:
 | 
			
		||||
        """
 | 
			
		||||
        Translate a tuple (R, G, B) into a curses color index.
 | 
			
		||||
        If we have already a color index, then nothing is processed.
 | 
			
		||||
        If this is a tuple, we construct a new color index if non-existing
 | 
			
		||||
        and we return this index.
 | 
			
		||||
        The values of R, G and B must be between 0 and 1000, and not
 | 
			
		||||
        between 0 and 255.
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(color, tuple):
 | 
			
		||||
            # The color is a tuple (R, G, B), that is potentially unknown.
 | 
			
		||||
            # We translate it into a curses color number.
 | 
			
		||||
            if color not in self._colors_rgb:
 | 
			
		||||
                # The color does not exist, we create it.
 | 
			
		||||
                color_nb = len(self._colors_rgb) + 8
 | 
			
		||||
                self.init_color(color_nb, color[0], color[1], color[2])
 | 
			
		||||
                self._colors_rgb[color] = color_nb
 | 
			
		||||
            color = self._colors_rgb[color]
 | 
			
		||||
        return color
 | 
			
		||||
 | 
			
		||||
    def addstr(self, pad: Any, y: int, x: int, msg: str,
 | 
			
		||||
               fg_color: Union[int, Tuple[int, int, int]] = curses.COLOR_WHITE,
 | 
			
		||||
               bg_color: Union[int, Tuple[int, int, int]] = curses.COLOR_BLACK,
 | 
			
		||||
               *, altcharset: bool = False, blink: bool = False,
 | 
			
		||||
               bold: bool = False, dim: bool = False, invis: bool = False,
 | 
			
		||||
               italic: bool = False, normal: bool = False,
 | 
			
		||||
               protect: bool = False, reverse: bool = False,
 | 
			
		||||
               standout: bool = False, underline: bool = False,
 | 
			
		||||
               horizontal: bool = False, left: bool = False,
 | 
			
		||||
               low: bool = False, right: bool = False, top: bool = False,
 | 
			
		||||
               vertical: bool = False, chartext: bool = False) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Display a message onto the pad.
 | 
			
		||||
        If the message is too large, it is truncated vertically and horizontally
 | 
			
		||||
        The text can be bold, italic, blinking, ... if the good parameters are
 | 
			
		||||
        given. These parameters are translated into curses attributes.
 | 
			
		||||
        The foreground and background colors can be given as curses constants
 | 
			
		||||
        (curses.COLOR_*), or by giving a tuple (R, G, B) that corresponds to
 | 
			
		||||
        the color. R, G, B must be between 0 and 1000, and not 0 and 255.
 | 
			
		||||
        """
 | 
			
		||||
        height, width = pad.getmaxyx()
 | 
			
		||||
        # Truncate message if it is too large
 | 
			
		||||
        msg = self.truncate(msg, height - y, width - x - 1)
 | 
			
		||||
        if msg.replace("\n", "") and x >= 0 and y >= 0:
 | 
			
		||||
            return pad.addstr(y, x, msg, *options)
 | 
			
		||||
            fg_color = self.translate_color(fg_color)
 | 
			
		||||
            bg_color = self.translate_color(bg_color)
 | 
			
		||||
 | 
			
		||||
            # Get the pair number for the tuple (fg, bg)
 | 
			
		||||
            # If it does not exist, create it and give a new unique id.
 | 
			
		||||
            if (fg_color, bg_color) in self._color_pairs:
 | 
			
		||||
                pair_nb = self._color_pairs[(fg_color, bg_color)]
 | 
			
		||||
            else:
 | 
			
		||||
                pair_nb = len(self._color_pairs)
 | 
			
		||||
                self.init_pair(pair_nb, fg_color, bg_color)
 | 
			
		||||
                self._color_pairs[(fg_color, bg_color)] = pair_nb
 | 
			
		||||
 | 
			
		||||
            # Compute curses attributes from the parameters
 | 
			
		||||
            attr = self.color_pair(pair_nb)
 | 
			
		||||
            attr |= curses.A_ALTCHARSET if altcharset else 0
 | 
			
		||||
            attr |= curses.A_BLINK if blink else 0
 | 
			
		||||
            attr |= curses.A_BOLD if bold else 0
 | 
			
		||||
            attr |= curses.A_DIM if dim else 0
 | 
			
		||||
            attr |= curses.A_INVIS if invis else 0
 | 
			
		||||
            attr |= curses.A_ITALIC if italic else 0
 | 
			
		||||
            attr |= curses.A_NORMAL if normal else 0
 | 
			
		||||
            attr |= curses.A_PROTECT if protect else 0
 | 
			
		||||
            attr |= curses.A_REVERSE if reverse else 0
 | 
			
		||||
            attr |= curses.A_STANDOUT if standout else 0
 | 
			
		||||
            attr |= curses.A_UNDERLINE if underline else 0
 | 
			
		||||
            attr |= curses.A_HORIZONTAL if horizontal else 0
 | 
			
		||||
            attr |= curses.A_LEFT if left else 0
 | 
			
		||||
            attr |= curses.A_LOW if low else 0
 | 
			
		||||
            attr |= curses.A_RIGHT if right else 0
 | 
			
		||||
            attr |= curses.A_TOP if top else 0
 | 
			
		||||
            attr |= curses.A_VERTICAL if vertical else 0
 | 
			
		||||
            attr |= curses.A_CHARTEXT if chartext else 0
 | 
			
		||||
 | 
			
		||||
            return pad.addstr(y, x, msg, attr)
 | 
			
		||||
 | 
			
		||||
    def init_pair(self, number: int, foreground: int, background: int) -> None:
 | 
			
		||||
        return curses.init_pair(number, foreground, background) \
 | 
			
		||||
@@ -156,17 +228,13 @@ class Box(Display):
 | 
			
		||||
        self.pad = self.newpad(self.rows, self.cols)
 | 
			
		||||
        self.fg_border_color = fg_border_color or curses.COLOR_WHITE
 | 
			
		||||
 | 
			
		||||
        pair_number = 4 + self.fg_border_color
 | 
			
		||||
        self.init_pair(pair_number, self.fg_border_color, curses.COLOR_BLACK)
 | 
			
		||||
        self.pair = self.color_pair(pair_number)
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        self.addstr(self.pad, 0, 0, "┏" + "━" * (self.width - 2) + "┓",
 | 
			
		||||
                    self.pair)
 | 
			
		||||
                    self.fg_border_color)
 | 
			
		||||
        for i in range(1, self.height - 1):
 | 
			
		||||
            self.addstr(self.pad, i, 0, "┃", self.pair)
 | 
			
		||||
            self.addstr(self.pad, i, self.width - 1, "┃", self.pair)
 | 
			
		||||
            self.addstr(self.pad, i, 0, "┃", self.fg_border_color)
 | 
			
		||||
            self.addstr(self.pad, i, self.width - 1, "┃", self.fg_border_color)
 | 
			
		||||
        self.addstr(self.pad, self.height - 1, 0,
 | 
			
		||||
                    "┗" + "━" * (self.width - 2) + "┛", self.pair)
 | 
			
		||||
                    "┗" + "━" * (self.width - 2) + "┛", self.fg_border_color)
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.y + self.height - 1, self.x + self.width - 1)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,15 +15,14 @@ class MapDisplay(Display):
 | 
			
		||||
        self.pad = self.newpad(m.height, self.pack.tile_width * m.width + 1)
 | 
			
		||||
 | 
			
		||||
    def update_pad(self) -> None:
 | 
			
		||||
        self.init_pair(1, self.pack.tile_fg_color, self.pack.tile_bg_color)
 | 
			
		||||
        self.init_pair(2, self.pack.entity_fg_color, self.pack.entity_bg_color)
 | 
			
		||||
        self.addstr(self.pad, 0, 0, self.map.draw_string(self.pack),
 | 
			
		||||
                    self.color_pair(1))
 | 
			
		||||
                    self.pack.tile_fg_color, self.pack.tile_bg_color)
 | 
			
		||||
        for e in self.map.entities:
 | 
			
		||||
            self.addstr(self.pad, e.y, self.pack.tile_width * e.x,
 | 
			
		||||
                        self.pack[e.name.upper()], self.color_pair(2))
 | 
			
		||||
                        self.pack[e.name.upper()],
 | 
			
		||||
                        self.pack.entity_fg_color, self.pack.entity_bg_color)
 | 
			
		||||
 | 
			
		||||
        # Display Path map for deubg purposes
 | 
			
		||||
        # Display Path map for debug purposes
 | 
			
		||||
        # from squirrelbattle.entities.player import Player
 | 
			
		||||
        # players = [ p for p in self.map.entities if isinstance(p,Player) ]
 | 
			
		||||
        # player = players[0] if len(players) > 0 else None
 | 
			
		||||
@@ -42,7 +41,8 @@ class MapDisplay(Display):
 | 
			
		||||
        #                 else:
 | 
			
		||||
        #                     character = '←'
 | 
			
		||||
        #                 self.addstr(self.pad, y, self.pack.tile_width * x,
 | 
			
		||||
        #                     character, self.color_pair(1))
 | 
			
		||||
        #                     character, self.pack.tile_fg_color,
 | 
			
		||||
        #                     self.pack.tile_bg_color)
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        y, x = self.map.currenty, self.pack.tile_width * self.map.currentx
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import curses
 | 
			
		||||
from random import randint
 | 
			
		||||
from typing import List
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.menus import Menu, MainMenu
 | 
			
		||||
@@ -30,9 +31,9 @@ class MenuDisplay(Display):
 | 
			
		||||
 | 
			
		||||
    def update_pad(self) -> None:
 | 
			
		||||
        for i in range(self.trueheight):
 | 
			
		||||
            self.addstr(self.pad, i, 0, "  " + self.values[i])
 | 
			
		||||
            self.addstr(self.pad, i, 0, "   " + self.values[i])
 | 
			
		||||
        # set a marker on the selected line
 | 
			
		||||
        self.addstr(self.pad, self.menu.position, 0, ">")
 | 
			
		||||
        self.addstr(self.pad, self.menu.position, 0, " >")
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        cornery = 0 if self.height - 2 >= self.menu.position - 1 \
 | 
			
		||||
@@ -43,7 +44,7 @@ class MenuDisplay(Display):
 | 
			
		||||
        self.menubox.refresh(self.y, self.x, self.height, self.width)
 | 
			
		||||
        self.pad.erase()
 | 
			
		||||
        self.update_pad()
 | 
			
		||||
        self.refresh_pad(self.pad, cornery, 0, self.y + 1, self.x + 2,
 | 
			
		||||
        self.refresh_pad(self.pad, cornery, 0, self.y + 1, self.x + 1,
 | 
			
		||||
                         self.height - 2 + self.y,
 | 
			
		||||
                         self.width - 2 + self.x)
 | 
			
		||||
 | 
			
		||||
@@ -102,8 +103,7 @@ class MainMenuDisplay(Display):
 | 
			
		||||
        self.pad = self.newpad(max(self.rows, len(self.title) + 30),
 | 
			
		||||
                               max(len(self.title[0]) + 5, self.cols))
 | 
			
		||||
 | 
			
		||||
        self.init_color(42, 1000, 1000, 1000)
 | 
			
		||||
        self.init_pair(42, 42, curses.COLOR_BLACK)
 | 
			
		||||
        self.fg_color = curses.COLOR_WHITE
 | 
			
		||||
 | 
			
		||||
        self.menudisplay = MenuDisplay(self.screen, self.pack)
 | 
			
		||||
        self.menudisplay.update_menu(self.menu)
 | 
			
		||||
@@ -112,7 +112,7 @@ class MainMenuDisplay(Display):
 | 
			
		||||
        for i in range(len(self.title)):
 | 
			
		||||
            self.addstr(self.pad, 4 + i, max(self.width // 2
 | 
			
		||||
                        - len(self.title[0]) // 2 - 1, 0), self.title[i],
 | 
			
		||||
                        self.color_pair(42))
 | 
			
		||||
                        self.fg_color)
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.height + self.y - 1,
 | 
			
		||||
                         self.width + self.x - 1)
 | 
			
		||||
@@ -131,9 +131,7 @@ class MainMenuDisplay(Display):
 | 
			
		||||
            self.menudisplay.handle_click(y - menuy, x - menux, game)
 | 
			
		||||
 | 
			
		||||
        if y <= len(self.title):
 | 
			
		||||
            from random import randint
 | 
			
		||||
            self.init_color(42, randint(0, 1000), randint(0, 1000),
 | 
			
		||||
                              randint(0, 1000))
 | 
			
		||||
            self.fg_color = randint(0, 1000), randint(0, 1000), randint(0, 1000)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlayerInventoryDisplay(MenuDisplay):
 | 
			
		||||
@@ -141,7 +139,7 @@ class PlayerInventoryDisplay(MenuDisplay):
 | 
			
		||||
 | 
			
		||||
    def update_pad(self) -> None:
 | 
			
		||||
        self.addstr(self.pad, 0, (self.width - len(self.message)) // 2,
 | 
			
		||||
                    self.message, curses.A_BOLD | curses.A_ITALIC)
 | 
			
		||||
                    self.message, bold=True, italic=True)
 | 
			
		||||
        for i, item in enumerate(self.menu.values):
 | 
			
		||||
            rep = self.pack[item.name.upper()]
 | 
			
		||||
            selection = f"[{rep}]" if i == self.menu.position else f" {rep} "
 | 
			
		||||
@@ -169,7 +167,7 @@ class StoreInventoryDisplay(MenuDisplay):
 | 
			
		||||
 | 
			
		||||
    def update_pad(self) -> None:
 | 
			
		||||
        self.addstr(self.pad, 0, (self.width - len(self.message)) // 2,
 | 
			
		||||
                    self.message, curses.A_BOLD | curses.A_ITALIC)
 | 
			
		||||
                    self.message, bold=True, italic=True)
 | 
			
		||||
        for i, item in enumerate(self.menu.values):
 | 
			
		||||
            rep = self.pack[item.name.upper()]
 | 
			
		||||
            selection = f"[{rep}]" if i == self.menu.position else f" {rep} "
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ class MessageDisplay(Display):
 | 
			
		||||
                         self.height + 2, self.width + 4)
 | 
			
		||||
        self.box.display()
 | 
			
		||||
        self.pad.erase()
 | 
			
		||||
        self.addstr(self.pad, 0, 0, self.message, curses.A_BOLD)
 | 
			
		||||
        self.addstr(self.pad, 0, 0, self.message, bold=True)
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.height + self.y - 1,
 | 
			
		||||
                         self.width + self.x - 1)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@ class StatsDisplay(Display):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.pad = self.newpad(self.rows, self.cols)
 | 
			
		||||
        self.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
 | 
			
		||||
 | 
			
		||||
    def update_player(self, p: Player) -> None:
 | 
			
		||||
        self.player = p
 | 
			
		||||
@@ -50,9 +49,8 @@ class StatsDisplay(Display):
 | 
			
		||||
                                    f"x{self.player.hazel}")
 | 
			
		||||
 | 
			
		||||
        if self.player.dead:
 | 
			
		||||
            self.addstr(self.pad, 11, 0, _("YOU ARE DEAD"),
 | 
			
		||||
                        curses.A_BOLD | curses.A_BLINK | curses.A_STANDOUT
 | 
			
		||||
                        | self.color_pair(3))
 | 
			
		||||
            self.addstr(self.pad, 11, 0, _("YOU ARE DEAD"), curses.COLOR_RED,
 | 
			
		||||
                        bold=True, blink=True, standout=True)
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        self.pad.erase()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user