Merge remote-tracking branch 'origin/master' into village
# Conflicts: # squirrelbattle/display/texturepack.py # squirrelbattle/interfaces.py
This commit is contained in:
		@@ -0,0 +1,2 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.game import Game
 | 
			
		||||
from squirrelbattle.display.display_manager import DisplayManager
 | 
			
		||||
from squirrelbattle.term_manager import TermManager
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,2 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import curses
 | 
			
		||||
from typing import Any, Optional, Union
 | 
			
		||||
 | 
			
		||||
@@ -19,6 +22,24 @@ class Display:
 | 
			
		||||
    def newpad(self, height: int, width: int) -> Union[FakePad, Any]:
 | 
			
		||||
        return curses.newpad(height, width) if self.screen else FakePad()
 | 
			
		||||
 | 
			
		||||
    def truncate(self, msg: str, height: int, width: int) -> str:
 | 
			
		||||
        height = max(0, height)
 | 
			
		||||
        width = max(0, width)
 | 
			
		||||
        lines = msg.split("\n")
 | 
			
		||||
        lines = lines[:height]
 | 
			
		||||
        lines = [line[:width] for line in lines]
 | 
			
		||||
        return "\n".join(lines)
 | 
			
		||||
 | 
			
		||||
    def addstr(self, pad: Any, y: int, x: int, msg: str, *options) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Display a message onto the pad.
 | 
			
		||||
        If the message is too large, it is truncated vertically and horizontally
 | 
			
		||||
        """
 | 
			
		||||
        height, width = pad.getmaxyx()
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
    def init_pair(self, number: int, foreground: int, background: int) -> None:
 | 
			
		||||
        return curses.init_pair(number, foreground, background) \
 | 
			
		||||
            if self.screen else None
 | 
			
		||||
@@ -32,14 +53,36 @@ class Display:
 | 
			
		||||
        self.y = y
 | 
			
		||||
        self.width = width
 | 
			
		||||
        self.height = height
 | 
			
		||||
        if hasattr(self, "pad") and resize_pad:
 | 
			
		||||
            self.pad.resize(self.height, self.width)
 | 
			
		||||
        if hasattr(self, "pad") and resize_pad and \
 | 
			
		||||
                self.height >= 0 and self.width >= 0:
 | 
			
		||||
            self.pad.resize(self.height + 1, self.width + 1)
 | 
			
		||||
 | 
			
		||||
    def refresh(self, *args, resize_pad: bool = True) -> None:
 | 
			
		||||
        if len(args) == 4:
 | 
			
		||||
            self.resize(*args, resize_pad)
 | 
			
		||||
        self.display()
 | 
			
		||||
 | 
			
		||||
    def refresh_pad(self, pad: Any, top_y: int, top_x: int,
 | 
			
		||||
                    window_y: int, window_x: int,
 | 
			
		||||
                    last_y: int, last_x: int) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Refresh a pad on a part of the window.
 | 
			
		||||
        The refresh starts at coordinates (top_y, top_x) from the pad,
 | 
			
		||||
        and is drawn from (window_y, window_x) to (last_y, last_x).
 | 
			
		||||
        If coordinates are invalid (negative indexes/length..., then nothing
 | 
			
		||||
        is drawn and no error is raised.
 | 
			
		||||
        """
 | 
			
		||||
        top_y, top_x = max(0, top_y), max(0, top_x)
 | 
			
		||||
        window_y, window_x = max(0, window_y), max(0, window_x)
 | 
			
		||||
        screen_max_y, screen_max_x = self.screen.getmaxyx() if self.screen \
 | 
			
		||||
            else (42, 42)
 | 
			
		||||
        last_y, last_x = min(screen_max_y - 1, last_y), \
 | 
			
		||||
            min(screen_max_x - 1, last_x)
 | 
			
		||||
 | 
			
		||||
        if last_y >= window_y and last_x >= window_x:
 | 
			
		||||
            # Refresh the pad only if coordinates are valid
 | 
			
		||||
            pad.refresh(top_y, top_x, window_y, window_x, last_y, last_x)
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
@@ -50,3 +93,68 @@ class Display:
 | 
			
		||||
    @property
 | 
			
		||||
    def cols(self) -> int:
 | 
			
		||||
        return curses.COLS if self.screen else 42
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VerticalSplit(Display):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.pad = self.newpad(self.rows, 1)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def width(self) -> int:
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
    @width.setter
 | 
			
		||||
    def width(self, val: Any) -> None:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        for i in range(self.height):
 | 
			
		||||
            self.addstr(self.pad, i, 0, "┃")
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.y + self.height - 1, self.x)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HorizontalSplit(Display):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.pad = self.newpad(1, self.cols)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def height(self) -> int:
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
    @height.setter
 | 
			
		||||
    def height(self, val: Any) -> None:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        for i in range(self.width):
 | 
			
		||||
            self.addstr(self.pad, 0, i, "━")
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x, self.y,
 | 
			
		||||
                         self.x + self.width - 1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Box(Display):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, fg_border_color: Optional[int] = None, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        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)
 | 
			
		||||
        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, self.height - 1, 0,
 | 
			
		||||
                    "┗" + "━" * (self.width - 2) + "┛", self.pair)
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.y + self.height - 1, self.x + self.width - 1)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import curses
 | 
			
		||||
from squirrelbattle.display.display import VerticalSplit, HorizontalSplit
 | 
			
		||||
from squirrelbattle.display.mapdisplay import MapDisplay
 | 
			
		||||
from squirrelbattle.display.messagedisplay import MessageDisplay
 | 
			
		||||
from squirrelbattle.display.statsdisplay import StatsDisplay
 | 
			
		||||
from squirrelbattle.display.menudisplay import SettingsMenuDisplay, \
 | 
			
		||||
    MainMenuDisplay
 | 
			
		||||
@@ -22,9 +27,12 @@ class DisplayManager:
 | 
			
		||||
                                               screen, pack)
 | 
			
		||||
        self.settingsmenudisplay = SettingsMenuDisplay(screen, pack)
 | 
			
		||||
        self.logsdisplay = LogsDisplay(screen, pack)
 | 
			
		||||
        self.messagedisplay = MessageDisplay(screen=screen, pack=None)
 | 
			
		||||
        self.hbar = HorizontalSplit(screen, pack)
 | 
			
		||||
        self.vbar = VerticalSplit(screen, pack)
 | 
			
		||||
        self.displays = [self.statsdisplay, self.mapdisplay,
 | 
			
		||||
                         self.mainmenudisplay, self.settingsmenudisplay,
 | 
			
		||||
                         self.logsdisplay]
 | 
			
		||||
                         self.logsdisplay, self.messagedisplay]
 | 
			
		||||
        self.update_game_components()
 | 
			
		||||
 | 
			
		||||
    def handle_display_action(self, action: DisplayActions) -> None:
 | 
			
		||||
@@ -40,20 +48,35 @@ class DisplayManager:
 | 
			
		||||
        self.statsdisplay.update_player(self.game.player)
 | 
			
		||||
        self.settingsmenudisplay.update_menu(self.game.settings_menu)
 | 
			
		||||
        self.logsdisplay.update_logs(self.game.logs)
 | 
			
		||||
        self.messagedisplay.update_message(self.game.message)
 | 
			
		||||
 | 
			
		||||
    def refresh(self) -> None:
 | 
			
		||||
        if self.game.state == GameMode.PLAY:
 | 
			
		||||
            # The map pad has already the good size
 | 
			
		||||
            self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.cols,
 | 
			
		||||
            self.mapdisplay.refresh(0, 0, self.rows * 4 // 5,
 | 
			
		||||
                                    self.mapdisplay.pack.tile_width
 | 
			
		||||
                                    * (self.cols * 4 // 5
 | 
			
		||||
                                       // self.mapdisplay.pack.tile_width),
 | 
			
		||||
                                    resize_pad=False)
 | 
			
		||||
            self.statsdisplay.refresh(self.rows * 4 // 5, 0,
 | 
			
		||||
                                      self.rows // 10, self.cols)
 | 
			
		||||
            self.logsdisplay.refresh(self.rows * 9 // 10, 0,
 | 
			
		||||
                                     self.rows // 10, self.cols)
 | 
			
		||||
            self.statsdisplay.refresh(0, self.cols * 4 // 5 + 1,
 | 
			
		||||
                                      self.rows, self.cols // 5 - 1)
 | 
			
		||||
            self.logsdisplay.refresh(self.rows * 4 // 5 + 1, 0,
 | 
			
		||||
                                     self.rows // 5 - 1, self.cols * 4 // 5)
 | 
			
		||||
            self.hbar.refresh(self.rows * 4 // 5, 0, 1, self.cols * 4 // 5)
 | 
			
		||||
            self.vbar.refresh(0, self.cols * 4 // 5, self.rows, 1)
 | 
			
		||||
        if self.game.state == GameMode.MAINMENU:
 | 
			
		||||
            self.mainmenudisplay.refresh(0, 0, self.rows, self.cols)
 | 
			
		||||
        if self.game.state == GameMode.SETTINGS:
 | 
			
		||||
            self.settingsmenudisplay.refresh(0, 0, self.rows, self.cols - 1)
 | 
			
		||||
 | 
			
		||||
        if self.game.message:
 | 
			
		||||
            height, width = 0, 0
 | 
			
		||||
            for line in self.game.message.split("\n"):
 | 
			
		||||
                height += 1
 | 
			
		||||
                width = max(width, len(line))
 | 
			
		||||
            y, x = (self.rows - height) // 2, (self.cols - width) // 2
 | 
			
		||||
            self.messagedisplay.refresh(y, x, height, width)
 | 
			
		||||
 | 
			
		||||
        self.resize_window()
 | 
			
		||||
 | 
			
		||||
    def resize_window(self) -> bool:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.display.display import Display
 | 
			
		||||
from squirrelbattle.interfaces import Logs
 | 
			
		||||
 | 
			
		||||
@@ -12,12 +15,11 @@ class LogsDisplay(Display):
 | 
			
		||||
        self.logs = logs
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        print(type(self.logs.messages), flush=True)
 | 
			
		||||
        messages = self.logs.messages[-self.height:]
 | 
			
		||||
        messages = messages[::-1]
 | 
			
		||||
        self.pad.clear()
 | 
			
		||||
        self.pad.erase()
 | 
			
		||||
        for i in range(min(self.height, len(messages))):
 | 
			
		||||
            self.pad.addstr(self.height - i - 1, self.x,
 | 
			
		||||
                            messages[i][:self.width])
 | 
			
		||||
        self.pad.refresh(0, 0, self.y, self.x, self.y + self.height,
 | 
			
		||||
                         self.x + self.width)
 | 
			
		||||
            self.addstr(self.pad, self.height - i - 1, self.x,
 | 
			
		||||
                        messages[i][:self.width])
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.y + self.height - 1, self.x + self.width - 1)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.interfaces import Map
 | 
			
		||||
from .display import Display
 | 
			
		||||
 | 
			
		||||
@@ -15,11 +17,11 @@ class MapDisplay(Display):
 | 
			
		||||
    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.pad.addstr(0, 0, self.map.draw_string(self.pack),
 | 
			
		||||
                        self.color_pair(1))
 | 
			
		||||
        self.addstr(self.pad, 0, 0, self.map.draw_string(self.pack),
 | 
			
		||||
                    self.color_pair(1))
 | 
			
		||||
        for e in self.map.entities:
 | 
			
		||||
            self.pad.addstr(e.y, self.pack.tile_width * e.x,
 | 
			
		||||
                            self.pack[e.name.upper()], self.color_pair(2))
 | 
			
		||||
            self.addstr(self.pad, e.y, self.pack.tile_width * e.x,
 | 
			
		||||
                        self.pack[e.name.upper()], self.color_pair(2))
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        y, x = self.map.currenty, self.pack.tile_width * self.map.currentx
 | 
			
		||||
@@ -31,9 +33,18 @@ class MapDisplay(Display):
 | 
			
		||||
        smaxrow = min(smaxrow, self.height - 1)
 | 
			
		||||
        smaxcol = self.pack.tile_width * self.map.width - \
 | 
			
		||||
            (x + deltax) + self.width - 1
 | 
			
		||||
 | 
			
		||||
        # Wrap perfectly the map according to the width of the tiles
 | 
			
		||||
        pmincol = self.pack.tile_width * (pmincol // self.pack.tile_width)
 | 
			
		||||
        smincol = self.pack.tile_width * (smincol // self.pack.tile_width)
 | 
			
		||||
        smaxcol = self.pack.tile_width \
 | 
			
		||||
            * (smaxcol // self.pack.tile_width + 1) - 1
 | 
			
		||||
 | 
			
		||||
        smaxcol = min(smaxcol, self.width - 1)
 | 
			
		||||
        pminrow = max(0, min(self.map.height, pminrow))
 | 
			
		||||
        pmincol = max(0, min(self.pack.tile_width * self.map.width, pmincol))
 | 
			
		||||
        self.pad.clear()
 | 
			
		||||
 | 
			
		||||
        self.pad.erase()
 | 
			
		||||
        self.update_pad()
 | 
			
		||||
        self.pad.refresh(pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol)
 | 
			
		||||
        self.refresh_pad(self.pad, pminrow, pmincol, sminrow, smincol, smaxrow,
 | 
			
		||||
                         smaxcol)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,12 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from typing import List
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.menus import Menu, MainMenu
 | 
			
		||||
from .display import Display
 | 
			
		||||
from .display import Display, Box
 | 
			
		||||
from ..resources import ResourceManager
 | 
			
		||||
from ..translations import gettext as _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MenuDisplay(Display):
 | 
			
		||||
@@ -11,25 +15,23 @@ class MenuDisplay(Display):
 | 
			
		||||
    """
 | 
			
		||||
    position: int
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args):
 | 
			
		||||
        super().__init__(*args)
 | 
			
		||||
        self.menubox = self.newpad(self.rows, self.cols)
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.menubox = Box(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def update_menu(self, menu: Menu) -> None:
 | 
			
		||||
        self.menu = menu
 | 
			
		||||
        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])
 | 
			
		||||
            self.addstr(self.pad, i, 0, "  " + self.values[i])
 | 
			
		||||
 | 
			
		||||
    def update_pad(self) -> None:
 | 
			
		||||
        for i in range(self.trueheight):
 | 
			
		||||
            self.pad.addstr(i, 0, "  " + self.values[i])
 | 
			
		||||
            self.addstr(self.pad, i, 0, "  " + self.values[i])
 | 
			
		||||
        # set a marker on the selected line
 | 
			
		||||
        self.pad.addstr(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 \
 | 
			
		||||
@@ -37,20 +39,21 @@ class MenuDisplay(Display):
 | 
			
		||||
            if self.height - 2 >= self.trueheight - self.menu.position else 0
 | 
			
		||||
 | 
			
		||||
        # 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.menubox.refresh(self.y, self.x, self.height, self.width)
 | 
			
		||||
        self.pad.erase()
 | 
			
		||||
        self.update_pad()
 | 
			
		||||
        self.pad.refresh(cornery, 0, self.y + 1, self.x + 2,
 | 
			
		||||
        self.refresh_pad(self.pad, cornery, 0, self.y + 1, self.x + 2,
 | 
			
		||||
                         self.height - 2 + self.y,
 | 
			
		||||
                         self.width - 2 + self.x)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def truewidth(self) -> int:
 | 
			
		||||
        return max([len(str(a)) for a in self.values])
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def trueheight(self) -> int:
 | 
			
		||||
        return len(self.values)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def preferred_width(self) -> int:
 | 
			
		||||
        return self.truewidth + 6
 | 
			
		||||
@@ -70,9 +73,10 @@ class SettingsMenuDisplay(MenuDisplay):
 | 
			
		||||
    """
 | 
			
		||||
    @property
 | 
			
		||||
    def values(self) -> List[str]:
 | 
			
		||||
        return [a[1][1] + (" : "
 | 
			
		||||
        return [_(a[1][1]) + (" : "
 | 
			
		||||
                + ("?" if self.menu.waiting_for_key
 | 
			
		||||
                    and a == self.menu.validate() else a[1][0])
 | 
			
		||||
                    and a == self.menu.validate() else a[1][0]
 | 
			
		||||
                   .replace("\n", "\\n"))
 | 
			
		||||
            if a[1][0] else "") for a in self.menu.values]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -95,9 +99,11 @@ class MainMenuDisplay(Display):
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        for i in range(len(self.title)):
 | 
			
		||||
            self.pad.addstr(4 + i, max(self.width // 2
 | 
			
		||||
                            - len(self.title[0]) // 2 - 1, 0), self.title[i])
 | 
			
		||||
        self.pad.refresh(0, 0, self.y, self.x, self.height, self.width)
 | 
			
		||||
            self.addstr(self.pad, 4 + i, max(self.width // 2
 | 
			
		||||
                        - len(self.title[0]) // 2 - 1, 0), self.title[i])
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.height + self.y - 1,
 | 
			
		||||
                         self.width + self.x - 1)
 | 
			
		||||
        menuwidth = min(self.menudisplay.preferred_width, self.width)
 | 
			
		||||
        menuy, menux = len(self.title) + 8, self.width // 2 - menuwidth // 2 - 1
 | 
			
		||||
        self.menudisplay.refresh(
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								squirrelbattle/display/messagedisplay.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								squirrelbattle/display/messagedisplay.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
import curses
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.display.display import Box, Display
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MessageDisplay(Display):
 | 
			
		||||
    """
 | 
			
		||||
    Display a message in a popup.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        self.box = Box(fg_border_color=curses.COLOR_RED, *args, **kwargs)
 | 
			
		||||
        self.message = ""
 | 
			
		||||
        self.pad = self.newpad(1, 1)
 | 
			
		||||
 | 
			
		||||
    def update_message(self, msg: str) -> None:
 | 
			
		||||
        self.message = msg
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        self.box.refresh(self.y - 1, self.x - 2,
 | 
			
		||||
                         self.height + 2, self.width + 4)
 | 
			
		||||
        self.box.display()
 | 
			
		||||
        self.pad.erase()
 | 
			
		||||
        self.addstr(self.pad, 0, 0, self.message, curses.A_BOLD)
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.height + self.y - 1,
 | 
			
		||||
                         self.width + self.x - 1)
 | 
			
		||||
@@ -1,9 +1,12 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import curses
 | 
			
		||||
 | 
			
		||||
from ..entities.player import Player
 | 
			
		||||
from ..translations import gettext as _
 | 
			
		||||
from .display import Display
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.entities.player import Player
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StatsDisplay(Display):
 | 
			
		||||
    player: Player
 | 
			
		||||
@@ -17,36 +20,28 @@ class StatsDisplay(Display):
 | 
			
		||||
        self.player = p
 | 
			
		||||
 | 
			
		||||
    def update_pad(self) -> None:
 | 
			
		||||
        string = ""
 | 
			
		||||
        for _ in range(self.width - 1):
 | 
			
		||||
            string = string + "-"
 | 
			
		||||
        self.pad.addstr(0, 0, string)
 | 
			
		||||
        string2 = "Player -- LVL {}  EXP {}/{}  HP {}/{}"\
 | 
			
		||||
        string2 = "Player -- LVL {}\nEXP {}/{}\nHP {}/{}"\
 | 
			
		||||
            .format(self.player.level, self.player.current_xp,
 | 
			
		||||
                    self.player.max_xp, self.player.health,
 | 
			
		||||
                    self.player.maxhealth)
 | 
			
		||||
        for _ in range(self.width - len(string2) - 1):
 | 
			
		||||
            string2 = string2 + " "
 | 
			
		||||
        self.pad.addstr(1, 0, string2)
 | 
			
		||||
        string3 = "Stats : STR {}  INT {}  CHR {}  DEX {} CON {}"\
 | 
			
		||||
        self.addstr(self.pad, 0, 0, string2)
 | 
			
		||||
        string3 = "STR {}\nINT {}\nCHR {}\nDEX {}\nCON {}"\
 | 
			
		||||
            .format(self.player.strength,
 | 
			
		||||
                    self.player.intelligence, self.player.charisma,
 | 
			
		||||
                    self.player.dexterity, self.player.constitution)
 | 
			
		||||
        for _ in range(self.width - len(string3) - 1):
 | 
			
		||||
            string3 = string3 + " "
 | 
			
		||||
        self.pad.addstr(2, 0, string3)
 | 
			
		||||
        self.addstr(self.pad, 3, 0, string3)
 | 
			
		||||
 | 
			
		||||
        inventory_str = "Inventaire : " + "".join(
 | 
			
		||||
        inventory_str = _("Inventory:") + " " + "".join(
 | 
			
		||||
            self.pack[item.name.upper()] for item in self.player.inventory)
 | 
			
		||||
        self.pad.addstr(3, 0, inventory_str)
 | 
			
		||||
        self.addstr(self.pad, 8, 0, inventory_str)
 | 
			
		||||
 | 
			
		||||
        if self.player.dead:
 | 
			
		||||
            self.pad.addstr(4, 0, "VOUS ÊTES MORT",
 | 
			
		||||
                            curses.A_BOLD | curses.A_BLINK | curses.A_STANDOUT
 | 
			
		||||
                            | self.color_pair(3))
 | 
			
		||||
            self.addstr(self.pad, 10, 0, _("YOU ARE DEAD"),
 | 
			
		||||
                        curses.A_BOLD | curses.A_BLINK | curses.A_STANDOUT
 | 
			
		||||
                        | self.color_pair(3))
 | 
			
		||||
 | 
			
		||||
    def display(self) -> None:
 | 
			
		||||
        self.pad.clear()
 | 
			
		||||
        self.pad.erase()
 | 
			
		||||
        self.update_pad()
 | 
			
		||||
        self.pad.refresh(0, 0, self.y, self.x,
 | 
			
		||||
                         4 + self.y, self.width + self.x)
 | 
			
		||||
        self.refresh_pad(self.pad, 0, 0, self.y, self.x,
 | 
			
		||||
                         self.y + self.height - 1, self.width + self.x - 1)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import curses
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
@@ -51,7 +54,7 @@ TexturePack.ASCII_PACK = TexturePack(
 | 
			
		||||
    HEART='❤',
 | 
			
		||||
    BOMB='o',
 | 
			
		||||
    RABBIT='Y',
 | 
			
		||||
    BEAVER='_',
 | 
			
		||||
    TIGER='n',
 | 
			
		||||
    TEDDY_BEAR='8',
 | 
			
		||||
    MERCHANT='M',
 | 
			
		||||
    SUNFLOWER='I',
 | 
			
		||||
@@ -67,12 +70,12 @@ TexturePack.SQUIRREL_PACK = TexturePack(
 | 
			
		||||
    EMPTY='  ',
 | 
			
		||||
    WALL='🧱',
 | 
			
		||||
    FLOOR='██',
 | 
			
		||||
    PLAYER='🐿️️',
 | 
			
		||||
    PLAYER='🐿️ ️',
 | 
			
		||||
    HEDGEHOG='🦔',
 | 
			
		||||
    HEART='💜',
 | 
			
		||||
    BOMB='💣',
 | 
			
		||||
    RABBIT='🐇',
 | 
			
		||||
    BEAVER='🦫',
 | 
			
		||||
    TIGER='🐅',
 | 
			
		||||
    TEDDY_BEAR='🧸',
 | 
			
		||||
    MERCHANT='🦜',
 | 
			
		||||
    SUNFLOWER='🌻',
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,2 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
from .player import Player
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from random import choice
 | 
			
		||||
 | 
			
		||||
from .player import Player
 | 
			
		||||
@@ -52,13 +55,13 @@ class Monster(FightingEntity):
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Beaver(Monster):
 | 
			
		||||
class Tiger(Monster):
 | 
			
		||||
    """
 | 
			
		||||
    A beaver monster
 | 
			
		||||
    A tiger monster
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, strength: int = 2, maxhealth: int = 20,
 | 
			
		||||
                 *args, **kwargs) -> None:
 | 
			
		||||
        super().__init__(name="beaver", strength=strength,
 | 
			
		||||
        super().__init__(name="tiger", strength=strength,
 | 
			
		||||
                         maxhealth=maxhealth, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from random import randint
 | 
			
		||||
from typing import Dict, Tuple
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from enum import Enum, auto
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,7 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from json import JSONDecodeError
 | 
			
		||||
from random import randint
 | 
			
		||||
from typing import Any, Optional
 | 
			
		||||
import json
 | 
			
		||||
@@ -10,6 +14,7 @@ from .interfaces import Map, Logs
 | 
			
		||||
from .resources import ResourceManager
 | 
			
		||||
from .settings import Settings
 | 
			
		||||
from . import menus
 | 
			
		||||
from .translations import gettext as _, Translator
 | 
			
		||||
from typing import Callable
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -27,13 +32,15 @@ class Game:
 | 
			
		||||
        Init the game.
 | 
			
		||||
        """
 | 
			
		||||
        self.state = GameMode.MAINMENU
 | 
			
		||||
        self.main_menu = menus.MainMenu()
 | 
			
		||||
        self.settings_menu = menus.SettingsMenu()
 | 
			
		||||
        self.settings = Settings()
 | 
			
		||||
        self.settings.load_settings()
 | 
			
		||||
        self.settings.write_settings()
 | 
			
		||||
        Translator.setlocale(self.settings.LOCALE)
 | 
			
		||||
        self.main_menu = menus.MainMenu()
 | 
			
		||||
        self.settings_menu = menus.SettingsMenu()
 | 
			
		||||
        self.settings_menu.update_values(self.settings)
 | 
			
		||||
        self.logs = Logs()
 | 
			
		||||
        self.message = None
 | 
			
		||||
 | 
			
		||||
    def new_game(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@@ -55,7 +62,7 @@ class Game:
 | 
			
		||||
        when the given key gets pressed.
 | 
			
		||||
        """
 | 
			
		||||
        while True:  # pragma no cover
 | 
			
		||||
            screen.clear()
 | 
			
		||||
            screen.erase()
 | 
			
		||||
            screen.refresh()
 | 
			
		||||
            self.display_actions(DisplayActions.REFRESH)
 | 
			
		||||
            key = screen.getkey()
 | 
			
		||||
@@ -68,6 +75,11 @@ class Game:
 | 
			
		||||
        Indicates what should be done when the given key is pressed,
 | 
			
		||||
        according to the current game state.
 | 
			
		||||
        """
 | 
			
		||||
        if self.message:
 | 
			
		||||
            self.message = None
 | 
			
		||||
            self.display_actions(DisplayActions.REFRESH)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if self.state == GameMode.PLAY:
 | 
			
		||||
            self.handle_key_pressed_play(key)
 | 
			
		||||
        elif self.state == GameMode.MAINMENU:
 | 
			
		||||
@@ -130,9 +142,24 @@ class Game:
 | 
			
		||||
        """
 | 
			
		||||
        Loads the game from a dictionary
 | 
			
		||||
        """
 | 
			
		||||
        self.map.load_state(d)
 | 
			
		||||
        # noinspection PyTypeChecker
 | 
			
		||||
        self.player = self.map.find_entities(Player)[0]
 | 
			
		||||
        try:
 | 
			
		||||
            self.map.load_state(d)
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            self.message = _("Some keys are missing in your save file.\n"
 | 
			
		||||
                             "Your save seems to be corrupt. It got deleted.")
 | 
			
		||||
            os.unlink(ResourceManager.get_config_path("save.json"))
 | 
			
		||||
            self.display_actions(DisplayActions.UPDATE)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        players = self.map.find_entities(Player)
 | 
			
		||||
        if not players:
 | 
			
		||||
            self.message = _("No player was found on this map!\n"
 | 
			
		||||
                             "Maybe you died?")
 | 
			
		||||
            self.player.health = 0
 | 
			
		||||
            self.display_actions(DisplayActions.UPDATE)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        self.player = players[0]
 | 
			
		||||
        self.display_actions(DisplayActions.UPDATE)
 | 
			
		||||
 | 
			
		||||
    def load_game(self) -> None:
 | 
			
		||||
@@ -142,7 +169,15 @@ class Game:
 | 
			
		||||
        file_path = ResourceManager.get_config_path("save.json")
 | 
			
		||||
        if os.path.isfile(file_path):
 | 
			
		||||
            with open(file_path, "r") as f:
 | 
			
		||||
                self.load_state(json.loads(f.read()))
 | 
			
		||||
                try:
 | 
			
		||||
                    state = json.loads(f.read())
 | 
			
		||||
                    self.load_state(state)
 | 
			
		||||
                except JSONDecodeError:
 | 
			
		||||
                    self.message = _("The JSON file is not correct.\n"
 | 
			
		||||
                                     "Your save seems corrupted. "
 | 
			
		||||
                                     "It got deleted.")
 | 
			
		||||
                    os.unlink(file_path)
 | 
			
		||||
                    self.display_actions(DisplayActions.UPDATE)
 | 
			
		||||
 | 
			
		||||
    def save_game(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
from random import randint
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from enum import Enum, auto
 | 
			
		||||
from math import sqrt
 | 
			
		||||
from random import choice, randint
 | 
			
		||||
from typing import List, Optional, Any
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.display.texturepack import TexturePack
 | 
			
		||||
from .display.texturepack import TexturePack
 | 
			
		||||
from .translations import gettext as _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Logs:
 | 
			
		||||
@@ -127,7 +129,7 @@ class Map:
 | 
			
		||||
        """
 | 
			
		||||
        Put randomly {count} hedgehogs on the map, where it is available.
 | 
			
		||||
        """
 | 
			
		||||
        for _ in range(count):
 | 
			
		||||
        for ignored in range(count):
 | 
			
		||||
            y, x = 0, 0
 | 
			
		||||
            while True:
 | 
			
		||||
                y, x = randint(0, self.height - 1), randint(0, self.width - 1)
 | 
			
		||||
@@ -319,16 +321,20 @@ class Entity:
 | 
			
		||||
        """
 | 
			
		||||
        return isinstance(self, FriendlyEntity)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def translated_name(self) -> str:
 | 
			
		||||
        return _(self.name.replace("_", " "))
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_all_entity_classes():
 | 
			
		||||
        """
 | 
			
		||||
        Returns all entities subclasses
 | 
			
		||||
        """
 | 
			
		||||
        from squirrelbattle.entities.items import Heart, Bomb
 | 
			
		||||
        from squirrelbattle.entities.monsters import Beaver, Hedgehog, \
 | 
			
		||||
        from squirrelbattle.entities.monsters import Tiger, Hedgehog, \
 | 
			
		||||
            Rabbit, TeddyBear
 | 
			
		||||
        from squirrelbattle.entities.friendly import Merchant,Sunflower
 | 
			
		||||
        return [Beaver, Bomb, Heart, Hedgehog, Rabbit, TeddyBear,Sunflower]
 | 
			
		||||
        return [Tiger, Bomb, Heart, Hedgehog, Rabbit, TeddyBear,Sunflower]
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_all_entity_classes_in_a_dict() -> dict:
 | 
			
		||||
@@ -336,12 +342,12 @@ class Entity:
 | 
			
		||||
        Returns all entities subclasses in a dictionary
 | 
			
		||||
        """
 | 
			
		||||
        from squirrelbattle.entities.player import Player
 | 
			
		||||
        from squirrelbattle.entities.monsters import Beaver, Hedgehog, Rabbit, \
 | 
			
		||||
        from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \
 | 
			
		||||
            TeddyBear
 | 
			
		||||
        from squirrelbattle.entities.items import Bomb, Heart
 | 
			
		||||
        from squirrelbattle.entities.friendly import Merchant,Sunflower
 | 
			
		||||
        return {
 | 
			
		||||
            "Beaver": Beaver,
 | 
			
		||||
            "Tiger": Tiger,
 | 
			
		||||
            "Bomb": Bomb,
 | 
			
		||||
            "Heart": Heart,
 | 
			
		||||
            "Hedgehog": Hedgehog,
 | 
			
		||||
@@ -399,8 +405,10 @@ class FightingEntity(Entity):
 | 
			
		||||
        """
 | 
			
		||||
        Deals damage to the opponent, based on the stats
 | 
			
		||||
        """
 | 
			
		||||
        return f"{self.name} hits {opponent.name}. "\
 | 
			
		||||
            + opponent.take_damage(self, self.strength)
 | 
			
		||||
        return _("{name} hits {opponent}.")\
 | 
			
		||||
            .format(name=_(self.translated_name.capitalize()),
 | 
			
		||||
                    opponent=_(opponent.translated_name)) + " " + \
 | 
			
		||||
            opponent.take_damage(self, self.strength)
 | 
			
		||||
 | 
			
		||||
    def take_damage(self, attacker: "Entity", amount: int) -> str:
 | 
			
		||||
        """
 | 
			
		||||
@@ -409,8 +417,11 @@ class FightingEntity(Entity):
 | 
			
		||||
        self.health -= amount
 | 
			
		||||
        if self.health <= 0:
 | 
			
		||||
            self.die()
 | 
			
		||||
        return f"{self.name} takes {amount} damage."\
 | 
			
		||||
            + (f" {self.name} dies." if self.health <= 0 else "")
 | 
			
		||||
        return _("{name} takes {amount} damage.")\
 | 
			
		||||
            .format(name=self.translated_name.capitalize(), amount=str(amount))\
 | 
			
		||||
            + (" " + _("{name} dies.")
 | 
			
		||||
               .format(name=self.translated_name.capitalize())
 | 
			
		||||
               if self.health <= 0 else "")
 | 
			
		||||
 | 
			
		||||
    def die(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@@ -451,5 +462,5 @@ class FriendlyEntity(Entity):
 | 
			
		||||
    def talk_to(self, player : Any) -> str:
 | 
			
		||||
        a = randint(0,len(self.dialogue_option)-1)
 | 
			
		||||
        return "The sunflower said : "+self.dialogue_option[a]
 | 
			
		||||
        
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										166
									
								
								squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
# SOME DESCRIPTIVE TITLE.
 | 
			
		||||
# Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# This file is distributed under the same license as the squirrelbattle package.
 | 
			
		||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
 | 
			
		||||
#
 | 
			
		||||
#, fuzzy
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
 | 
			
		||||
"POT-Creation-Date: 2020-11-28 16:03+0100\n"
 | 
			
		||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
			
		||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
			
		||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
			
		||||
"Language: \n"
 | 
			
		||||
"MIME-Version: 1.0\n"
 | 
			
		||||
"Content-Type: text/plain; charset=UTF-8\n"
 | 
			
		||||
"Content-Transfer-Encoding: 8bit\n"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/game_test.py:284 squirrelbattle/tests/game_test.py:287
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:16
 | 
			
		||||
msgid "New game"
 | 
			
		||||
msgstr "Neu Spiel"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:17
 | 
			
		||||
msgid "Resume"
 | 
			
		||||
msgstr "Weitergehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:18
 | 
			
		||||
msgid "Load"
 | 
			
		||||
msgstr "Laden"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:19
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr "Speichern"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:20
 | 
			
		||||
msgid "Settings"
 | 
			
		||||
msgstr "Einstellungen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:21
 | 
			
		||||
msgid "Exit"
 | 
			
		||||
msgstr "Verlassen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:27
 | 
			
		||||
msgid "Main key to move up"
 | 
			
		||||
msgstr "Haupttaste zum Obengehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:29
 | 
			
		||||
msgid "Secondary key to move up"
 | 
			
		||||
msgstr "Sekundärtaste zum Obengehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:31
 | 
			
		||||
msgid "Main key to move down"
 | 
			
		||||
msgstr "Haupttaste zum Untergehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:33
 | 
			
		||||
msgid "Secondary key to move down"
 | 
			
		||||
msgstr "Sekundärtaste zum Untergehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:35
 | 
			
		||||
msgid "Main key to move left"
 | 
			
		||||
msgstr "Haupttaste zum Linksgehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:37
 | 
			
		||||
msgid "Secondary key to move left"
 | 
			
		||||
msgstr "Sekundärtaste zum Linksgehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:39
 | 
			
		||||
msgid "Main key to move right"
 | 
			
		||||
msgstr "Haupttaste zum Rechtsgehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:41
 | 
			
		||||
msgid "Secondary key to move right"
 | 
			
		||||
msgstr "Sekundärtaste zum Rechtsgehen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:43
 | 
			
		||||
msgid "Key to validate a menu"
 | 
			
		||||
msgstr "Menütaste"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:45
 | 
			
		||||
msgid "Texture pack"
 | 
			
		||||
msgstr "Textur-Packung"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:46
 | 
			
		||||
msgid "Language"
 | 
			
		||||
msgstr "Sprache"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:49
 | 
			
		||||
msgid "player"
 | 
			
		||||
msgstr "Spieler"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:51
 | 
			
		||||
msgid "tiger"
 | 
			
		||||
msgstr "Tiger"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:52
 | 
			
		||||
msgid "hedgehog"
 | 
			
		||||
msgstr "Igel"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:53
 | 
			
		||||
msgid "rabbit"
 | 
			
		||||
msgstr "Kanninchen"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:54
 | 
			
		||||
msgid "teddy bear"
 | 
			
		||||
msgstr "Teddybär"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:56
 | 
			
		||||
msgid "bomb"
 | 
			
		||||
msgstr "Bombe"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:57
 | 
			
		||||
msgid "heart"
 | 
			
		||||
msgstr "Herz"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/display/statsdisplay.py:34
 | 
			
		||||
msgid "Inventory:"
 | 
			
		||||
msgstr "Bestand:"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/display/statsdisplay.py:39
 | 
			
		||||
msgid "YOU ARE DEAD"
 | 
			
		||||
msgstr "SIE WURDEN GESTORBEN"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:398
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} hits {opponent}."
 | 
			
		||||
msgstr "{name} schlägt {opponent}."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:410
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} takes {amount} damage."
 | 
			
		||||
msgstr "{name} nimmt {amount} Schadenspunkte."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:412
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} dies."
 | 
			
		||||
msgstr "{name} stirbt."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:71
 | 
			
		||||
msgid "Back"
 | 
			
		||||
msgstr "Zurück"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:148
 | 
			
		||||
msgid ""
 | 
			
		||||
"Some keys are missing in your save file.\n"
 | 
			
		||||
"Your save seems to be corrupt. It got deleted."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"In Ihrer Speicherdatei fehlen einige Schlüssel.\n"
 | 
			
		||||
"Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:156
 | 
			
		||||
msgid ""
 | 
			
		||||
"No player was found on this map!\n"
 | 
			
		||||
"Maybe you died?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Auf dieser Karte wurde kein Spieler gefunden!\n"
 | 
			
		||||
"Vielleicht sind Sie gestorben?"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:176
 | 
			
		||||
msgid ""
 | 
			
		||||
"The JSON file is not correct.\n"
 | 
			
		||||
"Your save seems corrupted. It got deleted."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Die JSON-Datei ist nicht korrekt.\n"
 | 
			
		||||
"Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht."
 | 
			
		||||
							
								
								
									
										195
									
								
								squirrelbattle/locale/en/LC_MESSAGES/squirrelbattle.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								squirrelbattle/locale/en/LC_MESSAGES/squirrelbattle.po
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,195 @@
 | 
			
		||||
# SOME DESCRIPTIVE TITLE.
 | 
			
		||||
# Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# This file is distributed under the same license as the squirrelbattle package.
 | 
			
		||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
 | 
			
		||||
#
 | 
			
		||||
#, fuzzy
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
 | 
			
		||||
"POT-Creation-Date: 2020-11-28 16:03+0100\n"
 | 
			
		||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
			
		||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
			
		||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
			
		||||
"Language: \n"
 | 
			
		||||
"MIME-Version: 1.0\n"
 | 
			
		||||
"Content-Type: text/plain; charset=UTF-8\n"
 | 
			
		||||
"Content-Transfer-Encoding: 8bit\n"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/display/statsdisplay.py:34
 | 
			
		||||
msgid "Inventory:"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/display/statsdisplay.py:39
 | 
			
		||||
msgid "YOU ARE DEAD"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:394 squirrelbattle/interfaces.py:398
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} hits {opponent}."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:405 squirrelbattle/interfaces.py:410
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} takes {amount} damage."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:45 squirrelbattle/tests/translations_test.py:14
 | 
			
		||||
#: squirrelbattle/tests/game_test.py:284 squirrelbattle/tests/game_test.py:287
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:16
 | 
			
		||||
msgid "New game"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:46 squirrelbattle/tests/translations_test.py:15
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:17
 | 
			
		||||
msgid "Resume"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:47 squirrelbattle/tests/translations_test.py:17
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:19
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:48 squirrelbattle/tests/translations_test.py:16
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:18
 | 
			
		||||
msgid "Load"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:49 squirrelbattle/tests/translations_test.py:18
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:20
 | 
			
		||||
msgid "Settings"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:50 squirrelbattle/tests/translations_test.py:19
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:21
 | 
			
		||||
msgid "Exit"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:71
 | 
			
		||||
msgid "Back"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:147 squirrelbattle/game.py:148
 | 
			
		||||
msgid ""
 | 
			
		||||
"Some keys are missing in your save file.\n"
 | 
			
		||||
"Your save seems to be corrupt. It got deleted."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:155 squirrelbattle/game.py:156
 | 
			
		||||
msgid ""
 | 
			
		||||
"No player was found on this map!\n"
 | 
			
		||||
"Maybe you died?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:175 squirrelbattle/game.py:176
 | 
			
		||||
msgid ""
 | 
			
		||||
"The JSON file is not correct.\n"
 | 
			
		||||
"Your save seems corrupted. It got deleted."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:21 squirrelbattle/tests/translations_test.py:21
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:25
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:27
 | 
			
		||||
msgid "Main key to move up"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:22 squirrelbattle/tests/translations_test.py:23
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:27
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:29
 | 
			
		||||
msgid "Secondary key to move up"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:23 squirrelbattle/tests/translations_test.py:25
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:29
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:31
 | 
			
		||||
msgid "Main key to move down"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:24 squirrelbattle/tests/translations_test.py:27
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:31
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:33
 | 
			
		||||
msgid "Secondary key to move down"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:25 squirrelbattle/tests/translations_test.py:29
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:33
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:35
 | 
			
		||||
msgid "Main key to move left"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:26 squirrelbattle/tests/translations_test.py:31
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:35
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:37
 | 
			
		||||
msgid "Secondary key to move left"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:27 squirrelbattle/tests/translations_test.py:33
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:37
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:39
 | 
			
		||||
msgid "Main key to move right"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:29 squirrelbattle/tests/translations_test.py:35
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:39
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:41
 | 
			
		||||
msgid "Secondary key to move right"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:30 squirrelbattle/tests/translations_test.py:37
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:41
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:43
 | 
			
		||||
msgid "Key to validate a menu"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:31 squirrelbattle/tests/translations_test.py:39
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:43
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:45
 | 
			
		||||
msgid "Texture pack"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:32 squirrelbattle/tests/translations_test.py:40
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:44
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:46
 | 
			
		||||
msgid "Language"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:407 squirrelbattle/interfaces.py:412
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} dies."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:47
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:49
 | 
			
		||||
msgid "player"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:49
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:51
 | 
			
		||||
msgid "tiger"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:50
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:52
 | 
			
		||||
msgid "hedgehog"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:51
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:53
 | 
			
		||||
msgid "rabbit"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:52
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:54
 | 
			
		||||
msgid "teddy bear"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:54
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:56
 | 
			
		||||
msgid "bomb"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:55
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:57
 | 
			
		||||
msgid "heart"
 | 
			
		||||
msgstr ""
 | 
			
		||||
							
								
								
									
										201
									
								
								squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,201 @@
 | 
			
		||||
# SOME DESCRIPTIVE TITLE.
 | 
			
		||||
# Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# This file is distributed under the same license as the squirrelbattle package.
 | 
			
		||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
 | 
			
		||||
#
 | 
			
		||||
#, fuzzy
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
 | 
			
		||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
 | 
			
		||||
"POT-Creation-Date: 2020-11-28 16:03+0100\n"
 | 
			
		||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
			
		||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
			
		||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
			
		||||
"Language: \n"
 | 
			
		||||
"MIME-Version: 1.0\n"
 | 
			
		||||
"Content-Type: text/plain; charset=UTF-8\n"
 | 
			
		||||
"Content-Transfer-Encoding: 8bit\n"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/display/statsdisplay.py:34
 | 
			
		||||
msgid "Inventory:"
 | 
			
		||||
msgstr "Inventaire :"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/display/statsdisplay.py:39
 | 
			
		||||
msgid "YOU ARE DEAD"
 | 
			
		||||
msgstr "VOUS ÊTES MORT"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:394 squirrelbattle/interfaces.py:398
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} hits {opponent}."
 | 
			
		||||
msgstr "{name} frappe {opponent}."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:405 squirrelbattle/interfaces.py:410
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} takes {amount} damage."
 | 
			
		||||
msgstr "{name} prend {amount} points de dégât."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:45 squirrelbattle/tests/translations_test.py:14
 | 
			
		||||
#: squirrelbattle/tests/game_test.py:284 squirrelbattle/tests/game_test.py:287
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:16
 | 
			
		||||
msgid "New game"
 | 
			
		||||
msgstr "Nouvelle partie"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:46 squirrelbattle/tests/translations_test.py:15
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:17
 | 
			
		||||
msgid "Resume"
 | 
			
		||||
msgstr "Continuer"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:47 squirrelbattle/tests/translations_test.py:17
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:19
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr "Sauvegarder"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:48 squirrelbattle/tests/translations_test.py:16
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:18
 | 
			
		||||
msgid "Load"
 | 
			
		||||
msgstr "Charger"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:49 squirrelbattle/tests/translations_test.py:18
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:20
 | 
			
		||||
msgid "Settings"
 | 
			
		||||
msgstr "Paramètres"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:50 squirrelbattle/tests/translations_test.py:19
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:21
 | 
			
		||||
msgid "Exit"
 | 
			
		||||
msgstr "Quitter"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/menus.py:71
 | 
			
		||||
msgid "Back"
 | 
			
		||||
msgstr "Retour"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:147 squirrelbattle/game.py:148
 | 
			
		||||
msgid ""
 | 
			
		||||
"Some keys are missing in your save file.\n"
 | 
			
		||||
"Your save seems to be corrupt. It got deleted."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Certaines clés de votre ficher de sauvegarde sont manquantes.\n"
 | 
			
		||||
"Votre sauvegarde semble corrompue. Elle a été supprimée."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:155 squirrelbattle/game.py:156
 | 
			
		||||
msgid ""
 | 
			
		||||
"No player was found on this map!\n"
 | 
			
		||||
"Maybe you died?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Aucun joueur n'a été trouvé sur la carte !\n"
 | 
			
		||||
"Peut-être êtes-vous mort ?"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/game.py:175 squirrelbattle/game.py:176
 | 
			
		||||
msgid ""
 | 
			
		||||
"The JSON file is not correct.\n"
 | 
			
		||||
"Your save seems corrupted. It got deleted."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Le fichier JSON de sauvegarde est incorrect.\n"
 | 
			
		||||
"Votre sauvegarde semble corrompue. Elle a été supprimée."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:21 squirrelbattle/tests/translations_test.py:21
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:25
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:27
 | 
			
		||||
msgid "Main key to move up"
 | 
			
		||||
msgstr "Touche principale pour aller vers le haut"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:22 squirrelbattle/tests/translations_test.py:23
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:27
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:29
 | 
			
		||||
msgid "Secondary key to move up"
 | 
			
		||||
msgstr "Touche secondaire pour aller vers le haut"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:23 squirrelbattle/tests/translations_test.py:25
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:29
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:31
 | 
			
		||||
msgid "Main key to move down"
 | 
			
		||||
msgstr "Touche principale pour aller vers le bas"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:24 squirrelbattle/tests/translations_test.py:27
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:31
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:33
 | 
			
		||||
msgid "Secondary key to move down"
 | 
			
		||||
msgstr "Touche secondaire pour aller vers le bas"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:25 squirrelbattle/tests/translations_test.py:29
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:33
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:35
 | 
			
		||||
msgid "Main key to move left"
 | 
			
		||||
msgstr "Touche principale pour aller vers la gauche"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:26 squirrelbattle/tests/translations_test.py:31
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:35
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:37
 | 
			
		||||
msgid "Secondary key to move left"
 | 
			
		||||
msgstr "Touche secondaire pour aller vers la gauche"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:27 squirrelbattle/tests/translations_test.py:33
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:37
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:39
 | 
			
		||||
msgid "Main key to move right"
 | 
			
		||||
msgstr "Touche principale pour aller vers la droite"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:29 squirrelbattle/tests/translations_test.py:35
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:39
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:41
 | 
			
		||||
msgid "Secondary key to move right"
 | 
			
		||||
msgstr "Touche secondaire pour aller vers la droite"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:30 squirrelbattle/tests/translations_test.py:37
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:41
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:43
 | 
			
		||||
msgid "Key to validate a menu"
 | 
			
		||||
msgstr "Touche pour valider un menu"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:31 squirrelbattle/tests/translations_test.py:39
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:43
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:45
 | 
			
		||||
msgid "Texture pack"
 | 
			
		||||
msgstr "Pack de textures"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/settings.py:32 squirrelbattle/tests/translations_test.py:40
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:44
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:46
 | 
			
		||||
msgid "Language"
 | 
			
		||||
msgstr "Langue"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/interfaces.py:407 squirrelbattle/interfaces.py:412
 | 
			
		||||
#, python-brace-format
 | 
			
		||||
msgid "{name} dies."
 | 
			
		||||
msgstr "{name} meurt."
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:47
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:49
 | 
			
		||||
msgid "player"
 | 
			
		||||
msgstr "joueur"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:49
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:51
 | 
			
		||||
msgid "tiger"
 | 
			
		||||
msgstr "tigre"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:50
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:52
 | 
			
		||||
msgid "hedgehog"
 | 
			
		||||
msgstr "hérisson"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:51
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:53
 | 
			
		||||
msgid "rabbit"
 | 
			
		||||
msgstr "lapin"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:52
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:54
 | 
			
		||||
msgid "teddy bear"
 | 
			
		||||
msgstr "nounours"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:54
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:56
 | 
			
		||||
msgid "bomb"
 | 
			
		||||
msgstr "bombe"
 | 
			
		||||
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:55
 | 
			
		||||
#: squirrelbattle/tests/translations_test.py:57
 | 
			
		||||
msgid "heart"
 | 
			
		||||
msgstr "cœur"
 | 
			
		||||
@@ -1,9 +1,13 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from enum import Enum
 | 
			
		||||
from typing import Any, Optional
 | 
			
		||||
 | 
			
		||||
from .display.texturepack import TexturePack
 | 
			
		||||
from .enums import GameMode, KeyValues, DisplayActions
 | 
			
		||||
from .settings import Settings
 | 
			
		||||
from .translations import gettext as _, Translator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Menu:
 | 
			
		||||
@@ -38,15 +42,15 @@ class MainMenuValues(Enum):
 | 
			
		||||
    """
 | 
			
		||||
    Values of the main menu
 | 
			
		||||
    """
 | 
			
		||||
    START = 'Nouvelle partie'
 | 
			
		||||
    RESUME = 'Continuer'
 | 
			
		||||
    SAVE = 'Sauvegarder'
 | 
			
		||||
    LOAD = 'Charger'
 | 
			
		||||
    SETTINGS = 'Paramètres'
 | 
			
		||||
    EXIT = 'Quitter'
 | 
			
		||||
    START = "New game"
 | 
			
		||||
    RESUME = "Resume"
 | 
			
		||||
    SAVE = "Save"
 | 
			
		||||
    LOAD = "Load"
 | 
			
		||||
    SETTINGS = "Settings"
 | 
			
		||||
    EXIT = "Exit"
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.value
 | 
			
		||||
        return _(self.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MainMenu(Menu):
 | 
			
		||||
@@ -64,7 +68,7 @@ class SettingsMenu(Menu):
 | 
			
		||||
 | 
			
		||||
    def update_values(self, settings: Settings) -> None:
 | 
			
		||||
        self.values = list(settings.__dict__.items())
 | 
			
		||||
        self.values.append(("RETURN", ["", "Retour"]))
 | 
			
		||||
        self.values.append(("RETURN", ["", _("Back")]))
 | 
			
		||||
 | 
			
		||||
    def handle_key_pressed(self, key: Optional[KeyValues], raw_key: str,
 | 
			
		||||
                           game: Any) -> None:
 | 
			
		||||
@@ -92,6 +96,12 @@ class SettingsMenu(Menu):
 | 
			
		||||
                            game.settings.TEXTURE_PACK)
 | 
			
		||||
                    game.settings.write_settings()
 | 
			
		||||
                    self.update_values(game.settings)
 | 
			
		||||
                elif option == "LOCALE":
 | 
			
		||||
                    game.settings.LOCALE = 'fr' if game.settings.LOCALE == 'en'\
 | 
			
		||||
                        else 'de' if game.settings.LOCALE == 'fr' else 'en'
 | 
			
		||||
                    Translator.setlocale(game.settings.LOCALE)
 | 
			
		||||
                    game.settings.write_settings()
 | 
			
		||||
                    self.update_values(game.settings)
 | 
			
		||||
                else:
 | 
			
		||||
                    self.waiting_for_key = True
 | 
			
		||||
                    self.update_values(game.settings)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,13 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
import locale
 | 
			
		||||
import os
 | 
			
		||||
from typing import Any, Generator
 | 
			
		||||
 | 
			
		||||
from .resources import ResourceManager
 | 
			
		||||
from .translations import gettext as _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Settings:
 | 
			
		||||
@@ -13,25 +18,17 @@ class Settings:
 | 
			
		||||
    We can define the setting by simply use settings.TEXTURE_PACK = 'new_key'
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.KEY_UP_PRIMARY = \
 | 
			
		||||
            ['z', 'Touche principale pour aller vers le haut']
 | 
			
		||||
        self.KEY_UP_SECONDARY = \
 | 
			
		||||
            ['KEY_UP', 'Touche secondaire pour aller vers le haut']
 | 
			
		||||
        self.KEY_DOWN_PRIMARY = \
 | 
			
		||||
            ['s', 'Touche principale pour aller vers le bas']
 | 
			
		||||
        self.KEY_DOWN_SECONDARY = \
 | 
			
		||||
            ['KEY_DOWN', 'Touche secondaire pour aller vers le bas']
 | 
			
		||||
        self.KEY_LEFT_PRIMARY = \
 | 
			
		||||
            ['q', 'Touche principale pour aller vers la gauche']
 | 
			
		||||
        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.KEY_ENTER = \
 | 
			
		||||
            ['\n', 'Touche pour valider un menu']
 | 
			
		||||
        self.TEXTURE_PACK = ['ascii', 'Pack de textures utilisé']
 | 
			
		||||
        self.KEY_UP_PRIMARY = ['z', 'Main key to move up']
 | 
			
		||||
        self.KEY_UP_SECONDARY = ['KEY_UP', 'Secondary key to move up']
 | 
			
		||||
        self.KEY_DOWN_PRIMARY = ['s', 'Main key to move down']
 | 
			
		||||
        self.KEY_DOWN_SECONDARY = ['KEY_DOWN', 'Secondary key to move down']
 | 
			
		||||
        self.KEY_LEFT_PRIMARY = ['q', 'Main key to move left']
 | 
			
		||||
        self.KEY_LEFT_SECONDARY = ['KEY_LEFT', 'Secondary key to move left']
 | 
			
		||||
        self.KEY_RIGHT_PRIMARY = ['d', 'Main key to move right']
 | 
			
		||||
        self.KEY_RIGHT_SECONDARY = ['KEY_RIGHT', 'Secondary key to move right']
 | 
			
		||||
        self.KEY_ENTER = ['\n', 'Key to validate a menu']
 | 
			
		||||
        self.TEXTURE_PACK = ['ascii', 'Texture pack']
 | 
			
		||||
        self.LOCALE = [locale.getlocale()[0][:2], 'Language']
 | 
			
		||||
 | 
			
		||||
    def __getattribute__(self, item: str) -> Any:
 | 
			
		||||
        superattribute = super().__getattribute__(item)
 | 
			
		||||
@@ -50,10 +47,10 @@ class Settings:
 | 
			
		||||
        Retrieve the comment of a setting.
 | 
			
		||||
        """
 | 
			
		||||
        if item in self.settings_keys:
 | 
			
		||||
            return object.__getattribute__(self, item)[1]
 | 
			
		||||
            return _(object.__getattribute__(self, item)[1])
 | 
			
		||||
        for key in self.settings_keys:
 | 
			
		||||
            if getattr(self, key) == item:
 | 
			
		||||
                return object.__getattribute__(self, key)[1]
 | 
			
		||||
                return _(object.__getattribute__(self, key)[1])
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def settings_keys(self) -> Generator[str, Any, None]:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import curses
 | 
			
		||||
from types import TracebackType
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,2 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,10 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.entities.items import Bomb, Heart, Item
 | 
			
		||||
from squirrelbattle.entities.monsters import Beaver, Hedgehog, Rabbit, TeddyBear
 | 
			
		||||
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, TeddyBear
 | 
			
		||||
from squirrelbattle.entities.player import Player
 | 
			
		||||
from squirrelbattle.interfaces import Entity, Map
 | 
			
		||||
from squirrelbattle.resources import ResourceManager
 | 
			
		||||
@@ -36,17 +39,17 @@ class TestEntities(unittest.TestCase):
 | 
			
		||||
        """
 | 
			
		||||
        Test some random stuff with fighting entities.
 | 
			
		||||
        """
 | 
			
		||||
        entity = Beaver()
 | 
			
		||||
        entity = Tiger()
 | 
			
		||||
        self.map.add_entity(entity)
 | 
			
		||||
        self.assertEqual(entity.maxhealth, 20)
 | 
			
		||||
        self.assertEqual(entity.maxhealth, entity.health)
 | 
			
		||||
        self.assertEqual(entity.strength, 2)
 | 
			
		||||
        for _ in range(9):
 | 
			
		||||
            self.assertEqual(entity.hit(entity),
 | 
			
		||||
                             "beaver hits beaver. beaver takes 2 damage.")
 | 
			
		||||
                             "Tiger hits tiger. Tiger takes 2 damage.")
 | 
			
		||||
            self.assertFalse(entity.dead)
 | 
			
		||||
        self.assertEqual(entity.hit(entity), "beaver hits beaver. "
 | 
			
		||||
                         + "beaver takes 2 damage. beaver dies.")
 | 
			
		||||
        self.assertEqual(entity.hit(entity), "Tiger hits tiger. "
 | 
			
		||||
                         + "Tiger takes 2 damage. Tiger dies.")
 | 
			
		||||
        self.assertTrue(entity.dead)
 | 
			
		||||
 | 
			
		||||
        entity = Rabbit()
 | 
			
		||||
@@ -67,8 +70,8 @@ class TestEntities(unittest.TestCase):
 | 
			
		||||
        self.assertTrue(entity.y == 2 and entity.x == 6)
 | 
			
		||||
        self.assertEqual(old_health - entity.strength, self.player.health)
 | 
			
		||||
        self.assertEqual(self.map.logs.messages[-1],
 | 
			
		||||
                         f"{entity.name} hits {self.player.name}. \
 | 
			
		||||
{self.player.name} takes {entity.strength} damage.")
 | 
			
		||||
                         f"{entity.name.capitalize()} hits {self.player.name}. \
 | 
			
		||||
{self.player.name.capitalize()} takes {entity.strength} damage.")
 | 
			
		||||
 | 
			
		||||
        # Fight the rabbit
 | 
			
		||||
        old_health = entity.health
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,19 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.bootstrap import Bootstrap
 | 
			
		||||
from squirrelbattle.display.display import Display
 | 
			
		||||
from squirrelbattle.display.display_manager import DisplayManager
 | 
			
		||||
from squirrelbattle.entities.player import Player
 | 
			
		||||
from squirrelbattle.game import Game, KeyValues, GameMode
 | 
			
		||||
from squirrelbattle.menus import MainMenuValues
 | 
			
		||||
from squirrelbattle.settings import Settings
 | 
			
		||||
from ..bootstrap import Bootstrap
 | 
			
		||||
from ..display.display import Display
 | 
			
		||||
from ..display.display_manager import DisplayManager
 | 
			
		||||
from ..entities.player import Player
 | 
			
		||||
from ..enums import DisplayActions
 | 
			
		||||
from ..game import Game, KeyValues, GameMode
 | 
			
		||||
from ..menus import MainMenuValues
 | 
			
		||||
from ..resources import ResourceManager
 | 
			
		||||
from ..settings import Settings
 | 
			
		||||
from ..translations import gettext as _, Translator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestGame(unittest.TestCase):
 | 
			
		||||
@@ -38,6 +44,27 @@ class TestGame(unittest.TestCase):
 | 
			
		||||
        new_state = self.game.save_state()
 | 
			
		||||
        self.assertEqual(old_state, new_state)
 | 
			
		||||
 | 
			
		||||
        # Error on loading save
 | 
			
		||||
        with open(ResourceManager.get_config_path("save.json"), "w") as f:
 | 
			
		||||
            f.write("I am not a JSON file")
 | 
			
		||||
        self.assertIsNone(self.game.message)
 | 
			
		||||
        self.game.load_game()
 | 
			
		||||
        self.assertIsNotNone(self.game.message)
 | 
			
		||||
        self.game.message = None
 | 
			
		||||
 | 
			
		||||
        with open(ResourceManager.get_config_path("save.json"), "w") as f:
 | 
			
		||||
            f.write("{}")
 | 
			
		||||
        self.assertIsNone(self.game.message)
 | 
			
		||||
        self.game.load_game()
 | 
			
		||||
        self.assertIsNotNone(self.game.message)
 | 
			
		||||
        self.game.message = None
 | 
			
		||||
 | 
			
		||||
        # Load game with a dead player
 | 
			
		||||
        self.game.map.remove_entity(self.game.player)
 | 
			
		||||
        self.game.save_game()
 | 
			
		||||
        self.game.load_game()
 | 
			
		||||
        self.assertIsNotNone(self.game.message)
 | 
			
		||||
 | 
			
		||||
    def test_bootstrap_fail(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Ensure that the test can't play the game,
 | 
			
		||||
@@ -247,12 +274,23 @@ class TestGame(unittest.TestCase):
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.ENTER)
 | 
			
		||||
        self.assertEqual(self.game.settings.TEXTURE_PACK, "ascii")
 | 
			
		||||
 | 
			
		||||
        # Change language
 | 
			
		||||
        Translator.compilemessages()
 | 
			
		||||
        Translator.refresh_translations()
 | 
			
		||||
        self.game.settings.LOCALE = "en"
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.DOWN)
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.ENTER)
 | 
			
		||||
        self.assertEqual(self.game.settings.LOCALE, "fr")
 | 
			
		||||
        self.assertEqual(_("New game"), "Nouvelle partie")
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.ENTER)
 | 
			
		||||
        self.assertEqual(self.game.settings.LOCALE, "de")
 | 
			
		||||
        self.assertEqual(_("New game"), "Neu Spiel")
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.ENTER)
 | 
			
		||||
        self.assertEqual(self.game.settings.LOCALE, "en")
 | 
			
		||||
        self.assertEqual(_("New game"), "New game")
 | 
			
		||||
 | 
			
		||||
        # Navigate to "back" button
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.DOWN)
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.DOWN)
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.DOWN)
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.DOWN)
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.DOWN)
 | 
			
		||||
 | 
			
		||||
        self.game.handle_key_pressed(KeyValues.ENTER)
 | 
			
		||||
        self.assertEqual(self.game.state, GameMode.MAINMENU)
 | 
			
		||||
@@ -289,3 +327,13 @@ class TestGame(unittest.TestCase):
 | 
			
		||||
        Check that some functions are not implemented, only for coverage.
 | 
			
		||||
        """
 | 
			
		||||
        self.assertRaises(NotImplementedError, Display.display, None)
 | 
			
		||||
 | 
			
		||||
    def test_messages(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Display error messages.
 | 
			
		||||
        """
 | 
			
		||||
        self.game.message = "I am an error"
 | 
			
		||||
        self.game.display_actions(DisplayActions.UPDATE)
 | 
			
		||||
        self.game.display_actions(DisplayActions.REFRESH)
 | 
			
		||||
        self.game.handle_key_pressed(None, "random key")
 | 
			
		||||
        self.assertIsNone(self.game.message)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.display.texturepack import TexturePack
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,9 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from typing import Tuple
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakePad:
 | 
			
		||||
    """
 | 
			
		||||
    In order to run tests, we simulate a fake curses pad that accepts functions
 | 
			
		||||
@@ -10,8 +16,11 @@ class FakePad:
 | 
			
		||||
                smincol: int, smaxrow: int, smaxcol: int) -> None:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def clear(self) -> None:
 | 
			
		||||
    def erase(self) -> None:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def resize(self, height: int, width: int) -> None:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def getmaxyx(self) -> Tuple[int, int]:
 | 
			
		||||
        return 42, 42
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.settings import Settings
 | 
			
		||||
@@ -21,7 +24,7 @@ class TestSettings(unittest.TestCase):
 | 
			
		||||
        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é')
 | 
			
		||||
                         'Texture pack')
 | 
			
		||||
 | 
			
		||||
        settings.TEXTURE_PACK = 'squirrel'
 | 
			
		||||
        self.assertEqual(settings.TEXTURE_PACK, 'squirrel')
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								squirrelbattle/tests/translations_test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								squirrelbattle/tests/translations_test.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from squirrelbattle.translations import gettext as _, Translator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestTranslations(unittest.TestCase):
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        Translator.compilemessages()
 | 
			
		||||
        Translator.refresh_translations()
 | 
			
		||||
        Translator.setlocale("fr")
 | 
			
		||||
 | 
			
		||||
    def test_main_menu_translation(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Ensure that the main menu is translated.
 | 
			
		||||
        """
 | 
			
		||||
        self.assertEqual(_("New game"), "Nouvelle partie")
 | 
			
		||||
        self.assertEqual(_("Resume"), "Continuer")
 | 
			
		||||
        self.assertEqual(_("Load"), "Charger")
 | 
			
		||||
        self.assertEqual(_("Save"), "Sauvegarder")
 | 
			
		||||
        self.assertEqual(_("Settings"), "Paramètres")
 | 
			
		||||
        self.assertEqual(_("Exit"), "Quitter")
 | 
			
		||||
 | 
			
		||||
    def test_settings_menu_translation(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Ensure that the settings menu is translated.
 | 
			
		||||
        """
 | 
			
		||||
        self.assertEqual(_("Main key to move up"),
 | 
			
		||||
                         "Touche principale pour aller vers le haut")
 | 
			
		||||
        self.assertEqual(_("Secondary key to move up"),
 | 
			
		||||
                         "Touche secondaire pour aller vers le haut")
 | 
			
		||||
        self.assertEqual(_("Main key to move down"),
 | 
			
		||||
                         "Touche principale pour aller vers le bas")
 | 
			
		||||
        self.assertEqual(_("Secondary key to move down"),
 | 
			
		||||
                         "Touche secondaire pour aller vers le bas")
 | 
			
		||||
        self.assertEqual(_("Main key to move left"),
 | 
			
		||||
                         "Touche principale pour aller vers la gauche")
 | 
			
		||||
        self.assertEqual(_("Secondary key to move left"),
 | 
			
		||||
                         "Touche secondaire pour aller vers la gauche")
 | 
			
		||||
        self.assertEqual(_("Main key to move right"),
 | 
			
		||||
                         "Touche principale pour aller vers la droite")
 | 
			
		||||
        self.assertEqual(_("Secondary key to move right"),
 | 
			
		||||
                         "Touche secondaire pour aller vers la droite")
 | 
			
		||||
        self.assertEqual(_("Key to validate a menu"),
 | 
			
		||||
                         "Touche pour valider un menu")
 | 
			
		||||
        self.assertEqual(_("Texture pack"), "Pack de textures")
 | 
			
		||||
        self.assertEqual(_("Language"), "Langue")
 | 
			
		||||
 | 
			
		||||
    def test_entities_translation(self) -> None:
 | 
			
		||||
        self.assertEqual(_("player"), "joueur")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(_("tiger"), "tigre")
 | 
			
		||||
        self.assertEqual(_("hedgehog"), "hérisson")
 | 
			
		||||
        self.assertEqual(_("rabbit"), "lapin")
 | 
			
		||||
        self.assertEqual(_("teddy bear"), "nounours")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(_("bomb"), "bombe")
 | 
			
		||||
        self.assertEqual(_("heart"), "cœur")
 | 
			
		||||
							
								
								
									
										96
									
								
								squirrelbattle/translations.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								squirrelbattle/translations.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import gettext as gt
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Any, List
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Translator:
 | 
			
		||||
    """
 | 
			
		||||
    This module uses gettext to translate strings.
 | 
			
		||||
    Translator.setlocale defines the language of the strings,
 | 
			
		||||
    then gettext() translates the message.
 | 
			
		||||
    """
 | 
			
		||||
    SUPPORTED_LOCALES: List[str] = ["de", "en", "fr"]
 | 
			
		||||
    locale: str = "en"
 | 
			
		||||
    translators: dict = {}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def refresh_translations(cls) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Load compiled translations.
 | 
			
		||||
        """
 | 
			
		||||
        for language in cls.SUPPORTED_LOCALES:
 | 
			
		||||
            rep = Path(__file__).parent / "locale" / language / "LC_MESSAGES"
 | 
			
		||||
            rep.mkdir(parents=True) if not rep.is_dir() else None
 | 
			
		||||
            if os.path.isfile(rep / "squirrelbattle.mo"):
 | 
			
		||||
                cls.translators[language] = gt.translation(
 | 
			
		||||
                    "squirrelbattle",
 | 
			
		||||
                    localedir=Path(__file__).parent / "locale",
 | 
			
		||||
                    languages=[language],
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setlocale(cls, lang: str) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Define the language used to translate the game.
 | 
			
		||||
        The language must be supported, otherwise nothing is done.
 | 
			
		||||
        """
 | 
			
		||||
        lang = lang[:2]
 | 
			
		||||
        if lang in cls.SUPPORTED_LOCALES:
 | 
			
		||||
            cls.locale = lang
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_translator(cls) -> Any:
 | 
			
		||||
        return cls.translators.get(cls.locale, gt.NullTranslations())
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def makemessages(cls) -> None:  # pragma: no cover
 | 
			
		||||
        """
 | 
			
		||||
        Analyse all strings in the project and extract them.
 | 
			
		||||
        """
 | 
			
		||||
        for language in cls.SUPPORTED_LOCALES:
 | 
			
		||||
            file_name = Path(__file__).parent / "locale" / language \
 | 
			
		||||
                / "LC_MESSAGES" / "squirrelbattle.po"
 | 
			
		||||
            args = ["find", "squirrelbattle", "-iname", "*.py"]
 | 
			
		||||
            find = subprocess.Popen(args, cwd=Path(__file__).parent.parent,
 | 
			
		||||
                                    stdout=subprocess.PIPE)
 | 
			
		||||
            args = ["xargs", "xgettext", "--from-code", "utf-8",
 | 
			
		||||
                    "--add-comments",
 | 
			
		||||
                    "--package-name=squirrelbattle",
 | 
			
		||||
                    "--package-version=3.14.1",
 | 
			
		||||
                    "--copyright-holder=ÿnérant, eichhornchen, "
 | 
			
		||||
                    "nicomarg, charlse",
 | 
			
		||||
                    "--msgid-bugs-address=squirrel-battle@crans.org",
 | 
			
		||||
                    "-o", file_name]
 | 
			
		||||
            if file_name.is_file():
 | 
			
		||||
                args.append("--join-existing")
 | 
			
		||||
            print(f"Make {language} messages...")
 | 
			
		||||
            subprocess.Popen(args, stdin=find.stdout).wait()
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def compilemessages(cls) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Compile translation messages from source files.
 | 
			
		||||
        """
 | 
			
		||||
        for language in cls.SUPPORTED_LOCALES:
 | 
			
		||||
            args = ["msgfmt", "--check-format",
 | 
			
		||||
                    "-o", Path(__file__).parent / "locale" / language
 | 
			
		||||
                    / "LC_MESSAGES" / "squirrelbattle.mo",
 | 
			
		||||
                    Path(__file__).parent / "locale" / language
 | 
			
		||||
                    / "LC_MESSAGES" / "squirrelbattle.po"]
 | 
			
		||||
            print(f"Compiling {language} messages...")
 | 
			
		||||
            subprocess.Popen(args).wait()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gettext(message: str) -> str:
 | 
			
		||||
    """
 | 
			
		||||
    Translate a message.
 | 
			
		||||
    """
 | 
			
		||||
    return Translator.get_translator().gettext(message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Translator.refresh_translations()
 | 
			
		||||
		Reference in New Issue
	
	Block a user