Merge branch 'master' into 'ladders'
# Conflicts: # squirrelbattle/game.py # squirrelbattle/interfaces.py # squirrelbattle/tests/game_test.py
This commit is contained in:
97
squirrelbattle/display/creditsdisplay.py
Normal file
97
squirrelbattle/display/creditsdisplay.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import curses
|
||||
|
||||
from ..display.display import Box, Display
|
||||
from ..game import Game
|
||||
from ..resources import ResourceManager
|
||||
from ..translations import gettext as _
|
||||
|
||||
|
||||
class CreditsDisplay(Display):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.box = Box(*args, **kwargs)
|
||||
self.pad = self.newpad(1, 1)
|
||||
self.ascii_art_displayed = False
|
||||
|
||||
def update(self, game: Game) -> None:
|
||||
return
|
||||
|
||||
def display(self) -> None:
|
||||
self.box.refresh(self.y, self.x, self.height, self.width)
|
||||
self.box.display()
|
||||
self.pad.erase()
|
||||
|
||||
messages = [
|
||||
_("Credits"),
|
||||
"",
|
||||
"Squirrel Battle",
|
||||
"",
|
||||
_("Developers:"),
|
||||
"Yohann \"ÿnérant\" D'ANELLO",
|
||||
"Mathilde \"eichhornchen\" DÉPRÉS",
|
||||
"Nicolas \"nicomarg\" MARGULIES",
|
||||
"Charles \"charsle\" PEYRAT",
|
||||
"",
|
||||
_("Translators:"),
|
||||
"Hugo \"ifugao\" JACOB (español)",
|
||||
]
|
||||
|
||||
for i, msg in enumerate(messages):
|
||||
self.addstr(self.pad, i + (self.height - len(messages)) // 2,
|
||||
(self.width - len(msg)) // 2, msg,
|
||||
bold=(i == 0), italic=(":" in msg))
|
||||
|
||||
if self.ascii_art_displayed:
|
||||
self.display_ascii_art()
|
||||
|
||||
self.refresh_pad(self.pad, 0, 0, self.y + 1, self.x + 1,
|
||||
self.height + self.y - 2,
|
||||
self.width + self.x - 2)
|
||||
|
||||
def display_ascii_art(self) -> None:
|
||||
with open(ResourceManager.get_asset_path("ascii-art-ecureuil.txt"))\
|
||||
as f:
|
||||
ascii_art = f.read().split("\n")
|
||||
|
||||
height, width = len(ascii_art), len(ascii_art[0])
|
||||
y_offset, x_offset = (self.height - height) // 2,\
|
||||
(self.width - width) // 2
|
||||
|
||||
for i, line in enumerate(ascii_art):
|
||||
for j, c in enumerate(line):
|
||||
bg_color = curses.COLOR_WHITE
|
||||
fg_color = curses.COLOR_BLACK
|
||||
bold = False
|
||||
if c == ' ':
|
||||
bg_color = curses.COLOR_BLACK
|
||||
elif c == '━' or c == '┃' or c == '⋀':
|
||||
bold = True
|
||||
fg_color = curses.COLOR_WHITE
|
||||
bg_color = curses.COLOR_BLACK
|
||||
elif c == '|':
|
||||
bold = True # c = '┃'
|
||||
fg_color = (100, 700, 1000)
|
||||
bg_color = curses.COLOR_BLACK
|
||||
elif c == '▓':
|
||||
fg_color = (700, 300, 0)
|
||||
elif c == '▒':
|
||||
fg_color = (700, 300, 0)
|
||||
bg_color = curses.COLOR_BLACK
|
||||
elif c == '░':
|
||||
fg_color = (350, 150, 0)
|
||||
elif c == '█':
|
||||
fg_color = (0, 0, 0)
|
||||
bg_color = curses.COLOR_BLACK
|
||||
elif c == '▬':
|
||||
c = '█'
|
||||
fg_color = (1000, 1000, 1000)
|
||||
bg_color = curses.COLOR_BLACK
|
||||
self.addstr(self.pad, y_offset + i, x_offset + j, c,
|
||||
fg_color, bg_color, bold=bold)
|
||||
|
||||
def handle_click(self, y: int, x: int, game: Game) -> None:
|
||||
if self.pad.inch(y - 1, x - 1) != ord(" "):
|
||||
self.ascii_art_displayed = True
|
@ -24,9 +24,16 @@ class Display:
|
||||
self.pack = pack or TexturePack.get_pack("ascii")
|
||||
|
||||
def newpad(self, height: int, width: int) -> Union[FakePad, Any]:
|
||||
"""
|
||||
Overwrites the native curses function of the same name.
|
||||
"""
|
||||
return curses.newpad(height, width) if self.screen else FakePad()
|
||||
|
||||
def truncate(self, msg: str, height: int, width: int) -> str:
|
||||
"""
|
||||
Truncates a string into a string adapted to the width and height of
|
||||
the screen.
|
||||
"""
|
||||
height = max(0, height)
|
||||
width = max(0, width)
|
||||
lines = msg.split("\n")
|
||||
@ -36,8 +43,8 @@ class Display:
|
||||
|
||||
def translate_color(self, color: Union[int, Tuple[int, int, int]]) -> int:
|
||||
"""
|
||||
Translate a tuple (R, G, B) into a curses color index.
|
||||
If we have already a color index, then nothing is processed.
|
||||
Translates a tuple (R, G, B) into a curses color index.
|
||||
If we already have a color index, then nothing is processed.
|
||||
If this is a tuple, we construct a new color index if non-existing
|
||||
and we return this index.
|
||||
The values of R, G and B must be between 0 and 1000, and not
|
||||
@ -66,9 +73,9 @@ class Display:
|
||||
low: bool = False, right: bool = False, top: bool = False,
|
||||
vertical: bool = False, chartext: bool = False) -> None:
|
||||
"""
|
||||
Display a message onto the pad.
|
||||
Displays a message onto the pad.
|
||||
If the message is too large, it is truncated vertically and horizontally
|
||||
The text can be bold, italic, blinking, ... if the good parameters are
|
||||
The text can be bold, italic, blinking, ... if the right parameters are
|
||||
given. These parameters are translated into curses attributes.
|
||||
The foreground and background colors can be given as curses constants
|
||||
(curses.COLOR_*), or by giving a tuple (R, G, B) that corresponds to
|
||||
@ -126,6 +133,9 @@ class Display:
|
||||
|
||||
def resize(self, y: int, x: int, height: int, width: int,
|
||||
resize_pad: bool = True) -> None:
|
||||
"""
|
||||
Resizes a pad.
|
||||
"""
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
@ -136,6 +146,9 @@ class Display:
|
||||
self.pad.resize(self.height + 1, self.width + 1)
|
||||
|
||||
def refresh(self, *args, resize_pad: bool = True) -> None:
|
||||
"""
|
||||
Refreshes a pad
|
||||
"""
|
||||
if len(args) == 4:
|
||||
self.resize(*args, resize_pad)
|
||||
self.display()
|
||||
@ -144,10 +157,10 @@ class Display:
|
||||
window_y: int, window_x: int,
|
||||
last_y: int, last_x: int) -> None:
|
||||
"""
|
||||
Refresh a pad on a part of the window.
|
||||
Refreshes 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
|
||||
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)
|
||||
@ -177,7 +190,7 @@ class Display:
|
||||
def handle_click(self, y: int, x: int, game: Game) -> None:
|
||||
"""
|
||||
A mouse click was performed on the coordinates (y, x) of the pad.
|
||||
Maybe it can do something.
|
||||
Maybe it should do something.
|
||||
"""
|
||||
pass
|
||||
|
||||
@ -191,7 +204,9 @@ class Display:
|
||||
|
||||
|
||||
class VerticalSplit(Display):
|
||||
|
||||
"""
|
||||
A class to split the screen in two vertically with a pretty line.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.pad = self.newpad(self.rows, 1)
|
||||
@ -212,7 +227,9 @@ class VerticalSplit(Display):
|
||||
|
||||
|
||||
class HorizontalSplit(Display):
|
||||
|
||||
"""
|
||||
A class to split the screen in two horizontally with a pretty line.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.pad = self.newpad(1, self.cols)
|
||||
@ -233,6 +250,9 @@ class HorizontalSplit(Display):
|
||||
|
||||
|
||||
class Box(Display):
|
||||
"""
|
||||
A class for pretty boxes to print menus and other content.
|
||||
"""
|
||||
title: str = ""
|
||||
|
||||
def update_title(self, title: str) -> None:
|
||||
|
@ -2,6 +2,8 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import curses
|
||||
|
||||
from squirrelbattle.display.creditsdisplay import CreditsDisplay
|
||||
from squirrelbattle.display.display import VerticalSplit, HorizontalSplit, \
|
||||
Display
|
||||
from squirrelbattle.display.mapdisplay import MapDisplay
|
||||
@ -30,17 +32,21 @@ class DisplayManager:
|
||||
self.mainmenudisplay = MainMenuDisplay(self.game.main_menu,
|
||||
screen, pack)
|
||||
self.settingsmenudisplay = SettingsMenuDisplay(screen, pack)
|
||||
self.messagedisplay = MessageDisplay(screen=screen, pack=None)
|
||||
self.messagedisplay = MessageDisplay(screen, pack)
|
||||
self.hbar = HorizontalSplit(screen, pack)
|
||||
self.vbar = VerticalSplit(screen, pack)
|
||||
self.creditsdisplay = CreditsDisplay(screen, pack)
|
||||
self.displays = [self.statsdisplay, self.mapdisplay,
|
||||
self.mainmenudisplay, self.settingsmenudisplay,
|
||||
self.logsdisplay, self.messagedisplay,
|
||||
self.playerinventorydisplay,
|
||||
self.storeinventorydisplay]
|
||||
self.storeinventorydisplay, self.creditsdisplay]
|
||||
self.update_game_components()
|
||||
|
||||
def handle_display_action(self, action: DisplayActions, *params) -> None:
|
||||
"""
|
||||
Handles the differents values of display action.
|
||||
"""
|
||||
if action == DisplayActions.REFRESH:
|
||||
self.refresh()
|
||||
elif action == DisplayActions.UPDATE:
|
||||
@ -58,6 +64,9 @@ class DisplayManager:
|
||||
d.update(self.game)
|
||||
|
||||
def handle_mouse_click(self, y: int, x: int) -> None:
|
||||
"""
|
||||
Handles the mouse clicks.
|
||||
"""
|
||||
displays = self.refresh()
|
||||
display = None
|
||||
for d in displays:
|
||||
@ -70,6 +79,9 @@ class DisplayManager:
|
||||
display.handle_click(y - display.y, x - display.x, self.game)
|
||||
|
||||
def refresh(self) -> List[Display]:
|
||||
"""
|
||||
Refreshes all components on the screen.
|
||||
"""
|
||||
displays = []
|
||||
pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK)
|
||||
|
||||
@ -118,6 +130,9 @@ class DisplayManager:
|
||||
elif self.game.state == GameMode.SETTINGS:
|
||||
self.settingsmenudisplay.refresh(0, 0, self.rows, self.cols)
|
||||
displays.append(self.settingsmenudisplay)
|
||||
elif self.game.state == GameMode.CREDITS:
|
||||
self.creditsdisplay.refresh(0, 0, self.rows, self.cols)
|
||||
displays.append(self.creditsdisplay)
|
||||
|
||||
if self.game.message:
|
||||
height, width = 0, 0
|
||||
@ -135,7 +150,7 @@ class DisplayManager:
|
||||
|
||||
def resize_window(self) -> bool:
|
||||
"""
|
||||
If the window got resized, ensure that the screen size got updated.
|
||||
When the window is resized, ensures that the screen size is updated.
|
||||
"""
|
||||
y, x = self.screen.getmaxyx() if self.screen else (0, 0)
|
||||
if self.screen and curses.is_term_resized(self.rows,
|
||||
@ -146,8 +161,16 @@ class DisplayManager:
|
||||
|
||||
@property
|
||||
def rows(self) -> int:
|
||||
"""
|
||||
Overwrites the native curses attribute of the same name,
|
||||
for testing purposes.
|
||||
"""
|
||||
return curses.LINES if self.screen else 42
|
||||
|
||||
@property
|
||||
def cols(self) -> int:
|
||||
"""
|
||||
Overwrites the native curses attribute of the same name,
|
||||
for testing purposes.
|
||||
"""
|
||||
return curses.COLS if self.screen else 42
|
||||
|
@ -7,6 +7,10 @@ from squirrelbattle.interfaces import Logs
|
||||
|
||||
|
||||
class LogsDisplay(Display):
|
||||
"""
|
||||
A class to handle the display of the logs.
|
||||
"""
|
||||
|
||||
logs: Logs
|
||||
|
||||
def __init__(self, *args) -> None:
|
||||
|
@ -7,6 +7,10 @@ from ..game import Game
|
||||
|
||||
|
||||
class MapDisplay(Display):
|
||||
"""
|
||||
A class to handle the display of the map.
|
||||
"""
|
||||
|
||||
map: Map
|
||||
|
||||
def __init__(self, *args):
|
||||
|
@ -16,7 +16,7 @@ from ..translations import gettext as _
|
||||
|
||||
class MenuDisplay(Display):
|
||||
"""
|
||||
A class to display the menu objects
|
||||
A class to display the menu objects.
|
||||
"""
|
||||
menu: Menu
|
||||
position: int
|
||||
@ -80,7 +80,7 @@ class MenuDisplay(Display):
|
||||
|
||||
class SettingsMenuDisplay(MenuDisplay):
|
||||
"""
|
||||
A class to display specifically a settingsmenu object
|
||||
A class to display specifically a settingsmenu object.
|
||||
"""
|
||||
menu: SettingsMenu
|
||||
|
||||
@ -98,7 +98,7 @@ class SettingsMenuDisplay(MenuDisplay):
|
||||
|
||||
class MainMenuDisplay(Display):
|
||||
"""
|
||||
A class to display specifically a mainmenu object
|
||||
A class to display specifically a mainmenu object.
|
||||
"""
|
||||
def __init__(self, menu: MainMenu, *args):
|
||||
super().__init__(*args)
|
||||
@ -120,6 +120,8 @@ class MainMenuDisplay(Display):
|
||||
self.addstr(self.pad, 4 + i, max(self.width // 2
|
||||
- len(self.title[0]) // 2 - 1, 0), self.title[i],
|
||||
self.fg_color)
|
||||
msg = _("Credits")
|
||||
self.addstr(self.pad, self.height - 1, self.width - 1 - len(msg), msg)
|
||||
self.refresh_pad(self.pad, 0, 0, self.y, self.x,
|
||||
self.height + self.y - 1,
|
||||
self.width + self.x - 1)
|
||||
@ -143,8 +145,14 @@ class MainMenuDisplay(Display):
|
||||
if y <= len(self.title):
|
||||
self.fg_color = randint(0, 1000), randint(0, 1000), randint(0, 1000)
|
||||
|
||||
if y == self.height - 1 and x >= self.width - 1 - len(_("Credits")):
|
||||
game.state = GameMode.CREDITS
|
||||
|
||||
|
||||
class PlayerInventoryDisplay(MenuDisplay):
|
||||
"""
|
||||
A class to handle the display of the player's inventory.
|
||||
"""
|
||||
player: Player = None
|
||||
selected: bool = True
|
||||
store_mode: bool = False
|
||||
@ -191,6 +199,9 @@ class PlayerInventoryDisplay(MenuDisplay):
|
||||
|
||||
|
||||
class StoreInventoryDisplay(MenuDisplay):
|
||||
"""
|
||||
A class to handle the display of a merchant's inventory.
|
||||
"""
|
||||
menu: StoreMenu
|
||||
selected: bool = False
|
||||
|
||||
|
@ -8,7 +8,7 @@ from squirrelbattle.game import Game
|
||||
|
||||
class MessageDisplay(Display):
|
||||
"""
|
||||
Display a message in a popup.
|
||||
A class to handle the display of popup messages.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -10,6 +10,9 @@ from .display import Display
|
||||
|
||||
|
||||
class StatsDisplay(Display):
|
||||
"""
|
||||
A class to handle the display of the stats of the player.
|
||||
"""
|
||||
player: Player
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -6,6 +6,9 @@ from typing import Any
|
||||
|
||||
|
||||
class TexturePack:
|
||||
"""
|
||||
A class to handle displaying several textures.
|
||||
"""
|
||||
_packs = dict()
|
||||
|
||||
name: str
|
||||
@ -29,6 +32,7 @@ class TexturePack:
|
||||
SWORD: str
|
||||
TEDDY_BEAR: str
|
||||
TIGER: str
|
||||
TRUMPET: str
|
||||
WALL: str
|
||||
|
||||
ASCII_PACK: "TexturePack"
|
||||
@ -75,6 +79,7 @@ TexturePack.ASCII_PACK = TexturePack(
|
||||
SWORD='\u2020',
|
||||
TEDDY_BEAR='8',
|
||||
TIGER='n',
|
||||
TRUMPET='/',
|
||||
WALL='#',
|
||||
)
|
||||
|
||||
@ -102,5 +107,6 @@ TexturePack.SQUIRREL_PACK = TexturePack(
|
||||
SWORD='🗡️ ',
|
||||
TEDDY_BEAR='🧸',
|
||||
TIGER='🐅',
|
||||
TRUMPET='🎺',
|
||||
WALL='🧱',
|
||||
)
|
||||
|
Reference in New Issue
Block a user