Merge remote-tracking branch 'origin/master' into village
# Conflicts: # squirrelbattle/display/texturepack.py # squirrelbattle/interfaces.py
This commit is contained in:
commit
29142cd91c
3
.gitignore
vendored
3
.gitignore
vendored
@ -23,3 +23,6 @@ save.json
|
||||
|
||||
# Don't commit docs output
|
||||
docs/_build
|
||||
|
||||
# Don't commit compiled messages
|
||||
*.mo
|
||||
|
@ -7,6 +7,7 @@ py37:
|
||||
stage: test
|
||||
image: python:3.7-alpine
|
||||
before_script:
|
||||
- apk add --no-cache gettext
|
||||
- pip install tox
|
||||
script: tox -e py3
|
||||
|
||||
@ -14,6 +15,7 @@ py38:
|
||||
stage: test
|
||||
image: python:3.8-alpine
|
||||
before_script:
|
||||
- apk add --no-cache gettext
|
||||
- pip install tox
|
||||
script: tox -e py3
|
||||
|
||||
@ -22,6 +24,7 @@ py39:
|
||||
stage: test
|
||||
image: python:3.9-alpine
|
||||
before_script:
|
||||
- apk add --no-cache gettext
|
||||
- pip install tox
|
||||
script: tox -e py3
|
||||
|
||||
@ -37,7 +40,7 @@ build-deb:
|
||||
image: debian:buster-slim
|
||||
stage: build
|
||||
before_script:
|
||||
- apt-get update && apt-get -y --no-install-recommends install build-essential debmake dh-python debhelper python3-all python3-setuptools
|
||||
- apt-get update && apt-get -y --no-install-recommends install build-essential debmake dh-python debhelper gettext python3-all python3-setuptools
|
||||
script:
|
||||
- dpkg-buildpackage
|
||||
- mkdir build && cp ../*.deb build/
|
||||
@ -45,3 +48,5 @@ build-deb:
|
||||
paths:
|
||||
- build/*.deb
|
||||
expire_in: 1 week
|
||||
only:
|
||||
- master
|
||||
|
@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
Squirrel Battle
|
||||
Copyright (C) 2020 ynerant
|
||||
Copyright (C) 2020 ÿnérant, eichhornchen, nicomarg, charlse
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
Squirrel Battle Copyright (C) 2020 ynerant
|
||||
Squirrel Battle Copyright (C) 2020 ÿnérant, eichhornchen, nicomarg, charlse
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
@ -1,7 +1,7 @@
|
||||
[![pipeline status](https://gitlab.crans.org/ynerant/squirrel-battle/badges/master/pipeline.svg)](https://gitlab.crans.org/ynerant/squirrel-battle/-/commits/master)
|
||||
[![coverage report](https://gitlab.crans.org/ynerant/squirrel-battle/badges/master/coverage.svg)](https://gitlab.crans.org/ynerant/squirrel-battle/-/commits/master)
|
||||
[![Documentation Status](https://readthedocs.org/projects/squirrel-battle/badge/?version=latest)](https://squirrel-battle.readthedocs.io/fr/latest/?badge=latest)
|
||||
[![PyPI](https://img.shields.io/pypi/v/dungeon-battle)](https://pypi.org/project/squirrel-battle/)
|
||||
[![PyPI](https://img.shields.io/pypi/v/squirrel-battle)](https://pypi.org/project/squirrel-battle/)
|
||||
[![PYPI downloads](https://img.shields.io/pypi/dm/squirrel-battle)](https://pypi.org/project/squirrel-battle/)
|
||||
[![AUR version](https://img.shields.io/aur/version/python-squirrel-battle)](https://aur.archlinux.org/packages/python-squirrel-battle/)
|
||||
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
2
debian/README.debian
vendored
2
debian/README.debian
vendored
@ -2,4 +2,4 @@ Squirrel Battle
|
||||
|
||||
Watch out for squirrel's knifes!
|
||||
|
||||
-- Yohann D'ANELLO <ynerant@crans.org> Thu, 19 Nov 2020 03:30:42 +0100
|
||||
-- Yohann D'ANELLO <squirrel-battle@crans.org> Thu, 19 Nov 2020 03:30:42 +0100
|
||||
|
10
debian/changelog
vendored
10
debian/changelog
vendored
@ -1,5 +1,11 @@
|
||||
python3-squirrel-battle (3.14) beta; urgency=low
|
||||
python3-squirrel-battle (3.14.1) beta; urgency=low
|
||||
|
||||
* Some graphical improvements.
|
||||
|
||||
-- Yohann D'ANELLO <squirrel-battle@crans.org> Thu, 27 Nov 2020 18:25:42 +0100
|
||||
|
||||
python3-squirrel-battle (3.14) beta; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Yohann D'ANELLO <ynerant@crans.org> Thu, 19 Nov 2020 03:30:42 +0100
|
||||
-- Yohann D'ANELLO <squirrel-battle@crans.org> Thu, 19 Nov 2020 03:30:42 +0100
|
||||
|
4
debian/control
vendored
4
debian/control
vendored
@ -1,8 +1,8 @@
|
||||
Source: python3-squirrel-battle
|
||||
Section: devel
|
||||
Priority: optional
|
||||
Maintainer: ynerant <ynrant@crans.org>
|
||||
Build-Depends: debhelper (>=10~), dh-python, python3-all, python3-setuptools
|
||||
Maintainer: ynerant <squirrel-battle@crans.org>
|
||||
Build-Depends: debhelper (>=10~), dh-python, gettext, python3-all, python3-setuptools
|
||||
Depends: fonts-noto-color-emoji
|
||||
Standards-Version: 4.1.4
|
||||
Homepage: https://gitlab.crans.org/ynerant/squirrel-battle
|
||||
|
6
debian/copyright
vendored
6
debian/copyright
vendored
@ -1,11 +1,11 @@
|
||||
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: Yohann D'ANELLO
|
||||
Upstream-Contact: Yohann D'ANELLO <ynerant@crans.org>
|
||||
Upstream-Name: ÿnérant, eichhornchen, nicomarg, charlse
|
||||
Upstream-Contact: ÿnérant, eichhornchen, nicomarg, charlse <squirrel-battle@crans.org>
|
||||
Source: https://gitlab.crans.org/ynerant/squirrel-battle
|
||||
|
||||
Files: *
|
||||
Copyright: 2020 Yohann D'ANELLO <ynerant@crans.org>
|
||||
Copyright: 2020 ÿnérant, eichhornchen, nicomarg, charlse <squirrel-battle@crans.org>
|
||||
License: GPL-3+
|
||||
This program is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public
|
||||
|
323
docs/deployment.rst
Normal file
323
docs/deployment.rst
Normal file
@ -0,0 +1,323 @@
|
||||
Déploiement du projet
|
||||
=====================
|
||||
|
||||
.. _PyPI: https://pypi.org/project/squirrel-battle/
|
||||
.. _AUR: https://aur.archlinux.org/packages/python-squirrel-battle/
|
||||
.. _Debian: https://gitlab.crans.org/ynerant/squirrel-battle/-/jobs/artifacts/master/raw/build/python3-squirrelbattle_3.14.1_all.deb?job=build-deb
|
||||
.. _installation: install.html
|
||||
|
||||
À chaque nouvelle version du projet, il est compilé et déployé dans PyPI_, dans
|
||||
l'AUR_ et un paquet Debian_ est créé, voir la page d'installation_.
|
||||
|
||||
|
||||
PyPI
|
||||
----
|
||||
|
||||
Définition du paquet
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. _setup.py: https://gitlab.crans.org/ynerant/squirrel-battle/-/blob/master/setup.py
|
||||
|
||||
La documentation sur le packaging dans PyPI_ est disponible `ici
|
||||
<https://packaging.python.org/tutorials/packaging-projects/>`_.
|
||||
|
||||
Le fichier `setup.py`_ contient l'ensemble des instructions d'installation du
|
||||
paquet ainsi que des détails à fournir à PyPI :
|
||||
|
||||
.. code:: python
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
with open("README.md", "r") as f:
|
||||
long_description = f.read()
|
||||
|
||||
# Compile messages
|
||||
for language in ["de", "en", "fr"]:
|
||||
args = ["msgfmt", "--check-format",
|
||||
"-o", f"squirrelbattle/locale/{language}/LC_MESSAGES"
|
||||
"/squirrelbattle.mo",
|
||||
f"squirrelbattle/locale/{language}/LC_MESSAGES"
|
||||
"/squirrelbattle.po"]
|
||||
print(f"Compiling {language} messages...")
|
||||
subprocess.Popen(args)
|
||||
|
||||
setup(
|
||||
name="squirrel-battle",
|
||||
version="3.14.1",
|
||||
author="ÿnérant, eichhornchen, nicomarg, charlse",
|
||||
author_email="squirrel-battle@crans.org",
|
||||
description="Watch out for squirrel's knives!",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://gitlab.crans.org/ynerant/squirrel-battle",
|
||||
packages=find_packages(),
|
||||
license='GPLv3',
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console :: Curses",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Natural Language :: French",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Topic :: Games/Entertainment",
|
||||
],
|
||||
python_requires='>=3.6',
|
||||
include_package_data=True,
|
||||
package_data={"squirrelbattle": ["assets/*", "locale/*/*/*.mo"]},
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"squirrel-battle = squirrelbattle.bootstrap:Bootstrap.run_game",
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
Ce fichier contient le nom du paquet, sa version, l'auteur et son contact,
|
||||
sa description en une ligne et sa description longue, le lien d'accueil du projet,
|
||||
sa licence, ses classificateurs et son exécutable.
|
||||
|
||||
Il commence tout d'abord par compiler les fichiers de `traduction <translation.html>`_.
|
||||
|
||||
Le paramètre ``entry_points`` définit un exécutable nommé ``squirrel-battle``,
|
||||
qui permet de lancer le jeu.
|
||||
|
||||
|
||||
Installation locale du paquet
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
L'installation du paquet localement dans son environnement Python (virtuel ou non)
|
||||
peut se faire en exécutant ``pip install -e .``.
|
||||
|
||||
|
||||
Génération des binaires
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Les paquets ``setuptools`` (``python3-setuptools`` pour APT, ``python-setuptools``
|
||||
pour pacman) et ``wheel`` (``python3-wheel`` pour APT, ``python-wheel`` pour pacman)
|
||||
sont nécessaires. Une fois installés, il faut appeler la commande :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python3 setup.py sdist bdist_wheel
|
||||
|
||||
Une fois cela fait, le dossier ``dist/`` contiendra les archives à transmettre à PyPI.
|
||||
|
||||
|
||||
Publier sur PyPI
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Il faut avant tout avoir un compte sur PyPI. Dans `votre compte PyPI
|
||||
<https://pypi.org/manage/account/>`_, il faut générer un jeton d'accès API.
|
||||
|
||||
Dans le fichier ``.pypirc`` dans le répertoire principal de l'utilisateur,
|
||||
il faut ajouter le jeton d'accès :
|
||||
|
||||
.. code::
|
||||
|
||||
[pypi]
|
||||
username = __token__
|
||||
password = pypi-my-pypi-api-access-token
|
||||
|
||||
Cela permet de s'authentifier directement par ce jeton.
|
||||
|
||||
Ensuite, il faut installer ``twine``, qui permet de publier sur PyPI.
|
||||
|
||||
Il suffit ensuite d'appeler :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
twine upload dist/*
|
||||
|
||||
pour envoyer le paquet sur PyPI.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
À des fins de tests, il est possible d'utiliser le dépôt `<https://test.pypi.org>`_.
|
||||
Les différences sont au niveau de l'authentification, où il faut l'en-tête
|
||||
``[testpypi]`` dans le ``.pypirc``, et il faut envoyer le paquet avec
|
||||
``twine upload --repository testpypi dist/``.
|
||||
|
||||
|
||||
Publier dans l'AUR
|
||||
------------------
|
||||
|
||||
Fonctionnement du packaging
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. _python-squirrel-battle: https://aur.archlinux.org/packages/python-squirrel-battle/
|
||||
.. _python-squirrel-battle-git: https://aur.archlinux.org/packages/python-squirrel-battle-git/
|
||||
|
||||
Deux paquets sont publiés dans l'AUR (Arch User Repository) :
|
||||
|
||||
- python-squirrel-battle_
|
||||
- python-squirrel-battle-git_
|
||||
|
||||
Le packaging dans Arch Linux se fait en commitant un fichier ``PKGBUILD`` dans
|
||||
le dépôt à l'adresse ``ssh://aur@aur.archlinux.org/packagename.git``,
|
||||
en remplaçant ``packagename`` par le nom du paquet.
|
||||
|
||||
Le second paquet compile directement le jeu à partir de la branche ``master``
|
||||
du dépôt Git. Le fichier ``PKGBUILD`` dispose de cette structure :
|
||||
|
||||
.. code::
|
||||
|
||||
# Maintainer: Yohann D'ANELLO <squirrel-battle@crans.org>
|
||||
|
||||
pkgbase=squirrel-battle
|
||||
pkgname=python-squirrel-battle-git
|
||||
pkgver=3.14.1
|
||||
pkgrel=1
|
||||
pkgdesc="Watch out for squirrel's knives!"
|
||||
arch=('any')
|
||||
url="https://gitlab.crans.org/ynerant/squirrel-battle"
|
||||
license=('GPLv3')
|
||||
depends=('python')
|
||||
makedepends=('gettext' 'python-setuptools')
|
||||
depends=('noto-fonts-emoji')
|
||||
checkdepends=('python-tox')
|
||||
ssource=("git+https://gitlab.crans.org/ynerant/squirrel-battle.git")
|
||||
sha256sums=("SKIP")
|
||||
|
||||
pkgver() {
|
||||
cd pkgbase
|
||||
git describe --long --tags | sed -r 's/^v//;s/([^-]*-g)/r\1/;s/-/./g'
|
||||
}
|
||||
build() {
|
||||
cd $pkgbase
|
||||
python setup.py build
|
||||
}
|
||||
|
||||
check() {
|
||||
cd $pkgbase
|
||||
tox -e py3
|
||||
tox -e linters
|
||||
}
|
||||
|
||||
package() {
|
||||
cd $pkgbase
|
||||
python setup.py install --skip-build \
|
||||
--optimize=1 \
|
||||
--root="${pkgdir}"
|
||||
install -vDm 644 README.md \
|
||||
-t "${pkgdir}/usr/share/doc/${pkgname}"
|
||||
install -vDm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
|
||||
Ces instructions permettent de cloner le dépôt, l'installer et exécuter des tests,
|
||||
en plus de définir les attributs du paquet.
|
||||
|
||||
Le fichier ``PKGBUILD`` du paquet ``python-squirrel-battle``, synchronisé avec
|
||||
les releases, est plus ou moins similaire :
|
||||
|
||||
.. code::
|
||||
|
||||
# Maintainer: Yohann D'ANELLO <squirrel-battle@crans.org>
|
||||
|
||||
pkgbase=squirrel-battle
|
||||
pkgname=python-squirrel-battle
|
||||
pkgver=3.14.1
|
||||
pkgrel=1
|
||||
pkgdesc="Watch out for squirrel's knives!"
|
||||
arch=('any')
|
||||
url="https://gitlab.crans.org/ynerant/squirrel-battle"
|
||||
license=('GPLv3')
|
||||
depends=('python')
|
||||
makedepends=('gettext' 'python-setuptools')
|
||||
depends=('noto-fonts-emoji')
|
||||
checkdepends=('python-tox')
|
||||
source=("https://gitlab.crans.org/ynerant/squirrel-battle/-/archive/v3.14.1/$pkgbase-v$pkgver.tar.gz")
|
||||
sha256sums=("6090534d598c0b3a8f5acdb553c12908ba8107d62d08e17747d1dbb397bddef0")
|
||||
|
||||
build() {
|
||||
cd $pkgbase-v$pkgver
|
||||
python setup.py build
|
||||
}
|
||||
|
||||
check() {
|
||||
cd $pkgbase-v$pkgver
|
||||
tox -e py3
|
||||
tox -e linters
|
||||
}
|
||||
|
||||
package() {
|
||||
cd $pkgbase-v$pkgver
|
||||
python setup.py install --skip-build \
|
||||
--optimize=1 \
|
||||
--root="${pkgdir}"
|
||||
install -vDm 644 README.md \
|
||||
-t "${pkgdir}/usr/share/doc/${pkgname}"
|
||||
install -vDm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||
}
|
||||
|
||||
Il se contente ici de télécharger l'archive de la dernière release, et de travailler
|
||||
dessus.
|
||||
|
||||
|
||||
Mettre à jour
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Pour mettre à jour le dépôt, une fois les dépôts
|
||||
``ssh://aur@aur.archlinux.org/python-squirrel-battle.git`` et
|
||||
``ssh://aur@aur.archlinux.org/python-squirrel-battle-git.git`` clonés,
|
||||
il suffit de mettre à jour le paramètre ``pkgver`` pour la bonne version,
|
||||
de régénérer le fichier ``.SRCINFO`` en faisant
|
||||
``makepkg --printsrcinfo > .SRCINFO``, puis de committer/pousser.
|
||||
|
||||
|
||||
Construction du paquet Debian
|
||||
-----------------------------
|
||||
|
||||
Structure du paquet
|
||||
-------------------
|
||||
|
||||
L'ensemble des instructions pour construire le paquet Debian est situé dans le
|
||||
dossier ``debian/``.
|
||||
|
||||
Le fichier ``changelog`` est à modifier à chaque nouvelle version, le fichier
|
||||
``compat`` contient la version minimale de Debian requise (``10`` pour Debian
|
||||
Buster), le fichier ``copyright`` contient la liste des fichiers distribués sous
|
||||
quelle licence (ici GPLv3), le fichier ``control`` contient les informations du
|
||||
paquet, le fichier ``install`` les fichiers de configuration à installer
|
||||
(ici le fix de l'affichage de l'écurueil), et enfin le fichier ``rules`` l'ensemble
|
||||
des instructions à exécuter pour installer.
|
||||
|
||||
Le paquet ``fonts-noto-color-emoji`` est en dépendance pour le bon affichage
|
||||
des émojis.
|
||||
|
||||
Mettre à jour le paquet
|
||||
-----------------------
|
||||
|
||||
Pour changer la version du paquet, il faut ajouter des lignes dans le fichier
|
||||
``changelog``.
|
||||
|
||||
|
||||
Construire le paquet
|
||||
--------------------
|
||||
|
||||
Il faut partir d'une installation de Debian.
|
||||
|
||||
D'abord on installe les paquets nécessaires :
|
||||
|
||||
.. code::
|
||||
|
||||
apt update
|
||||
apt --no-install-recommends install build-essential debmake dh-python debhelper gettext python3-all python3-setuptools
|
||||
|
||||
On peut ensuite construire le paquet :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
dpkg-buildpackage
|
||||
mkdir build && cp ../*.deb build/
|
||||
|
||||
Le paquet sera installé dans ``build/python3-squirrel-battle_3.14.1_all.deb``.
|
||||
|
||||
Le paquet Debian_ est construit par l'intégration continue Gitlab et ajouté
|
||||
à chaque release.
|
31
docs/documentation.rst
Normal file
31
docs/documentation.rst
Normal file
@ -0,0 +1,31 @@
|
||||
Documentation
|
||||
=============
|
||||
|
||||
La documentation est gérée grâce à Sphinx. Le thème est le thème officiel de
|
||||
ReadTheDocs ``sphinx-rtd-theme``.
|
||||
|
||||
Générer localement la documentation
|
||||
-----------------------------------
|
||||
|
||||
On commence par se rendre au bon endroit et installer les bonnes dépendances :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
cd docs
|
||||
pip install -r requirements.txt
|
||||
|
||||
La documentation se génère à partir d'appels à ``make``, selon le type de
|
||||
documentation voulue.
|
||||
|
||||
Par exemple, ``make html`` construit la documentation web, ``make latexpdf``
|
||||
construit un livre PDF avec cette documentation.
|
||||
|
||||
|
||||
Documentation externe
|
||||
---------------------
|
||||
|
||||
À chaque commit, un webhook est envoyé à `<readthedocs.io>`_, qui construit
|
||||
tout seul la documentation Sphinx, la publiant à l'adresse
|
||||
`<squirrel-battle.readthedocs.io>`_.
|
||||
|
||||
De plus, les documentations sont sauvegardées à chaque release taguée.
|
@ -25,14 +25,14 @@ Dans le `pack de textures`_ ASCII, il est représenté par le caractère ``*``.
|
||||
Dans le `pack de textures`_ écureuil, il est représenté par l'émoji ``🦔``.
|
||||
|
||||
|
||||
Castor
|
||||
------
|
||||
Tigre
|
||||
-----
|
||||
|
||||
Son nom est fixé à `beaver`. Il a par défaut une force à **2** et **20** points de vie.
|
||||
Son nom est fixé à `tiger`. Il a par défaut une force à **2** et **20** points de vie.
|
||||
|
||||
Dans le `pack de textures`_ ASCII, il est représenté par le caractère ``_``.
|
||||
Dans le `pack de textures`_ ASCII, il est représenté par le caractère ``n``.
|
||||
|
||||
Dans le `pack de textures`_ écureuil, il est représenté par l'émoji ``🦫``.
|
||||
Dans le `pack de textures`_ écureuil, il est représenté par l'émoji ``🐅``.
|
||||
|
||||
|
||||
Lapin
|
||||
|
@ -13,11 +13,11 @@ Bienvenue dans la documentation de Squirrel Battle !
|
||||
:target: https://squirrel-battle.readthedocs.io/fr/latest/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/dungeon-battle
|
||||
.. image:: https://img.shields.io/pypi/v/squirrel-battle
|
||||
:target: https://pypi.org/project/squirrel-battle/
|
||||
:alt: PyPI
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/dungeon-battle
|
||||
.. image:: https://img.shields.io/pypi/dm/squirrel-battle
|
||||
:target: https://pypi.org/project/squirrel-battle/
|
||||
:alt: PyPI downloads
|
||||
|
||||
@ -37,6 +37,9 @@ Bienvenue dans la documentation de Squirrel Battle !
|
||||
install-dev
|
||||
tests
|
||||
display/index
|
||||
translation
|
||||
deployment
|
||||
documentation
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
@ -1,16 +1,19 @@
|
||||
Installation d'un environnement de développement
|
||||
================================================
|
||||
|
||||
Il est toujours préférable de travailler dans un environnement Python isolé du reste de son instalation.
|
||||
Il est toujours préférable de travailler dans un environnement Python isolé du
|
||||
reste de son instalation.
|
||||
|
||||
1. **Installation des dépendances de la distribution.**
|
||||
Vous devez déjà installer Python et le module qui permet de créer des environnements virtuels.
|
||||
On donne ci-dessous l'exemple pour une distribution basée sur Debian, mais vous pouvez facilement adapter pour ArchLinux ou autre.
|
||||
Vous devez déjà installer Python et le module qui permet de créer des
|
||||
environnements virtuels.
|
||||
On donne ci-dessous l'exemple pour une distribution basée sur Debian,
|
||||
mais vous pouvez facilement adapter pour ArchLinux ou autre.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ sudo apt update
|
||||
$ sudo apt install --no-install-recommends -y python3-setuptools python3-venv python3-dev git
|
||||
$ sudo apt install --no-install-recommends -y python3-setuptools python3-venv python3-dev gettext git
|
||||
|
||||
2. **Clonage du dépot** là où vous voulez :
|
||||
|
||||
@ -25,7 +28,13 @@ Il est toujours préférable de travailler dans un environnement Python isolé d
|
||||
|
||||
$ python3 -m venv env
|
||||
$ source env/bin/activate # entrer dans l'environnement
|
||||
(env)$ pip3 install -r requirements.txt
|
||||
(env)$ deactivate # sortir de l'environnement
|
||||
(env) $ pip3 install -r requirements.txt
|
||||
(env) $ deactivate # sortir de l'environnement
|
||||
|
||||
4. **Compilation des messages de traduction.**
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(env) $ python3 main.py --compilemessages
|
||||
|
||||
Le lancement du jeu se fait en lançant la commande ``python3 main.py``.
|
@ -61,29 +61,19 @@ Le jeu peut être ensuite lancé via la commande ``squirrel-battle``.
|
||||
Sur Ubuntu/Debian
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. _paquet: https://gitlab.crans.org/ynerant/squirrel-battle/-/jobs/artifacts/master/raw/build/python3-squirrelbattle_3.14_all.deb?job=build-deb
|
||||
.. _paquet: https://gitlab.crans.org/ynerant/squirrel-battle/-/jobs/artifacts/master/raw/build/python3-squirrelbattle_3.14.1_all.deb?job=build-deb
|
||||
|
||||
Un paquet_ est généré par l'intégration continue de Gitlab à chaque commit.
|
||||
Ils sont également attachés à chaque nouvelle release.
|
||||
|
||||
Il dépend du paquet ``fonts-noto-color-emoji``, permettant d'afficher les émojis
|
||||
dans le terminal. Il peut être installé via APT normalement sur une distribution
|
||||
récente, toutefois sur les versions les plus vieilles, incluant Debian Buster,
|
||||
certains émojis n'apparaissent pas. Il est essentiel de maintenir ce paquet à
|
||||
jour. Pour installer manuellement la dernière version de ce paquet,
|
||||
il suffit d'exécuter :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
wget http://ftp.fr.debian.org/debian/pool/main/f/fonts-noto-color-emoji/fonts-noto-color-emoji_0~20200916-1_all.deb
|
||||
dpkg -i fonts-noto-color-emoji_0~20200916-1_all.deb
|
||||
rm fonts-noto-color-emoji_0~20200916-1_all.deb
|
||||
dans le terminal. Il peut être installé via APT.
|
||||
|
||||
Pour installer ce paquet, il suffit de le télécharger et d'appeler ``dpkg`` :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
dpkg -i python3-squirrelbattle_3.14_all.deb
|
||||
dpkg -i python3-squirrelbattle_3.14.1_all.deb
|
||||
|
||||
Ce paquet inclut un patch pour afficher les émojis écureuil correctement.
|
||||
|
||||
|
@ -12,7 +12,7 @@ Pack de textures
|
||||
.. _Cœur: entities/items.html#coeur
|
||||
.. _Bombe: entities/items.html#bombe
|
||||
.. _Lapin: entities/monsters.html#lapin
|
||||
.. _Castor: entities/monsters.html#castor
|
||||
.. _Tigre: entities/monsters.html#tigre
|
||||
.. _Nounours: entities/monsters.html#nounours
|
||||
|
||||
Chaque entité_ et chaque tuile_ de la carte_ est associé à un caractère pour
|
||||
@ -42,7 +42,7 @@ Chaque tuile fait un caractère de large.
|
||||
* Cœur_ : ``❤``
|
||||
* Bombe_ : ``o``
|
||||
* Lapin_ : ``Y``
|
||||
* Castor_ : ``_``
|
||||
* Tigre_ : ``n``
|
||||
* Nounours_ : ``8``
|
||||
|
||||
|
||||
@ -61,5 +61,5 @@ Chaque tuile fait 2 caractères de large pour afficher les émojis proprement.
|
||||
* Cœur_ : ``💜``
|
||||
* Bombe_ : ``💣``
|
||||
* Lapin_ : ``🐇``
|
||||
* Castor_ : ``🦫``
|
||||
* Tigre_ : ``🐅``
|
||||
* Nounours_ : ``🧸``
|
||||
|
120
docs/translation.rst
Normal file
120
docs/translation.rst
Normal file
@ -0,0 +1,120 @@
|
||||
Traduction
|
||||
==========
|
||||
|
||||
Le jeu Squirrel Battle est entièrement traduit en anglais, en français et en allement.
|
||||
La langue se choisit dans les `paramètres <settings.html>`_.
|
||||
|
||||
|
||||
Utitisation
|
||||
-----------
|
||||
|
||||
Les traductions sont gérées grâce au module natif ``gettext``. Le module
|
||||
``squirrelbattle.translations`` s'occupe d'installer les traductions, et de
|
||||
donner les chaînes traduites.
|
||||
|
||||
Pour choisir la langue, il faut appeler ``Translator.setlocale(language: str)``,
|
||||
où ``language`` correspond au code à 2 lettres de la langue.
|
||||
|
||||
Enfin, le module expose une fonction ``gettext(str) -> str`` qui permet de
|
||||
traduire les chaînes.
|
||||
|
||||
Il est courant et recommandé d'importer cette fonction sous l'alias ``_``,
|
||||
afin de limiter la verbositer et de permettre de rendre facilement une chaîne
|
||||
traduisible.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from squirrelbattle.translations import gettext as _, Translator
|
||||
|
||||
Translator.setlocale("fr")
|
||||
print(_("I am a translatable string"))
|
||||
print("I am not translatable")
|
||||
|
||||
Si les traductions sont bien faites (voir ci-dessous), cela donnera :
|
||||
|
||||
.. code::
|
||||
|
||||
Je suis une chaîne traduisible
|
||||
I am not translatable
|
||||
|
||||
À noter que si la chaîne n'est pas traduite, alors par défaut on renvoie la
|
||||
chaîne elle-même.
|
||||
|
||||
|
||||
Extraction des chaînes à traduire
|
||||
---------------------------------
|
||||
|
||||
L'appel à ``gettext`` ne fait pas que traduire les chaînes : il est possible
|
||||
également d'extraire toutes les chaînes à traduire.
|
||||
|
||||
Il est nécessaire d'installer le paquet Linux ``gettext`` pour cela.
|
||||
|
||||
L'utilitaire ``xgettext`` s'occupe de cette extraction. Il s'utilise de la façon
|
||||
suivante :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
xgettext --from-code utf-8 -o output_file.po source_1.py ... source_n.py
|
||||
|
||||
Afin de ne pas avoir à sélectionner manuellement chaque fichier, il est possible
|
||||
d'appeler directement ``python3 main.py --makemessages``. Cela a pour effet
|
||||
d'exécuter pour chaque langue ``<LANG>`` :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
find squirrelbattle -iname '*.py' | 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 squirrelbattle/locale/<LANG>/LC_MESSAGES/squirrelbattle.po
|
||||
|
||||
Les fichiers de traductions se trouvent alors dans
|
||||
``squirrelbattle/locale/<LANG>/LC_MESSAGES/squirrelbattle.po``.
|
||||
|
||||
|
||||
Traduire les chaînes
|
||||
--------------------
|
||||
|
||||
Après extraction des chaînes, les chaînes à traduire se trouvent dans
|
||||
``squirrelbattle/locale/<LANG>/LC_MESSAGES/squirrelbattle.po``, comme indiqué
|
||||
ci-dessus.
|
||||
|
||||
Ce fichier peut-être édité avec un utilitaire tel que ``poedit``, sur
|
||||
l'interface Web sur `<https://translate.ynerant.fr/squirrel-battle/squirrel-battle>`_,
|
||||
mais surtout manuellement avec un éditeur de texte.
|
||||
|
||||
Dans ce fichier, on obtient pour chaque chaîne à traduire un paragraphe de la
|
||||
forme :
|
||||
|
||||
.. code:: po
|
||||
|
||||
#: main.py:4
|
||||
msgid "I am a translatable string"
|
||||
msgstr "Je suis une chaîne traduisible"
|
||||
|
||||
Il sufift de remplir les champs ``msgstr``.
|
||||
|
||||
|
||||
Compilation des chaînes
|
||||
-----------------------
|
||||
|
||||
Pour gagner en efficacité, les chaînes sont compilées dans un fichier avec
|
||||
l'extension ``.mo``. Ce sont ces fichiers qui sont lus par le module de traduction.
|
||||
|
||||
Pour compiler les traductions, c'est l'utilitaire ``msgfmt`` fourni toujours par
|
||||
le paquet Linux ``gettext`` que nous utilisons. Il s'utilise assez simplement :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
msgfmt po_file.po -o mo_file.mo
|
||||
|
||||
À nouveau, il est possible de compiler automatiquement les messages en exécutant
|
||||
``python3 main.py --compilemessages``.
|
||||
|
||||
.. warning::
|
||||
|
||||
On ne partagera pas dans le dépôt Git les fichiers compilé. En développement,
|
||||
on compilera soi-même les messages, et en production, la construction des
|
||||
paquets se charge de compiler automatiquement les traductions.
|
@ -24,21 +24,12 @@ Sous Ubuntu/Debian
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
À nouveau, le terminal `xfce4-terminal` est recommandé. Le paquet
|
||||
`fonts-noto-color-emoji`. Toutefois, le rythme de mise à jour de Debian étant
|
||||
lent, le paquet le plus récent ne contient pas tous les émojis. Sur Debian,
|
||||
il faudra donc installer le paquet le plus récent, ce qui fonctionne sans
|
||||
dépendance supplémentaire :
|
||||
`fonts-noto-color-emoji`.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
wget http://ftp.fr.debian.org/debian/pool/main/f/fonts-noto-color-emoji/fonts-noto-color-emoji_0~20200916-1_all.deb
|
||||
dpkg -i fonts-noto-color-emoji_0~20200916-1_all.deb
|
||||
rm fonts-noto-color-emoji_0~20200916-1_all.deb
|
||||
|
||||
Il reste le problème de l'écureuil. Sous Ubuntu et Debian, le caractère écureuil
|
||||
existe déjà, mais ne s'affiche pas proprement. On peut appliquer un patch qui
|
||||
permet d'afficher les émojis correctement dans son terminal. Pour cela, il
|
||||
suffit de faire :
|
||||
Toutefois, un problème reste avec l'écureuil. Sous Ubuntu et Debian, le
|
||||
caractère écureuil existe déjà, mais ne s'affiche pas proprement. On peut
|
||||
appliquer un patch qui permet d'afficher les émojis correctement dans son
|
||||
terminal. Pour cela, il suffit de faire :
|
||||
|
||||
.. code:: bash
|
||||
|
||||
|
24
main.py
24
main.py
@ -1,5 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from squirrelbattle.bootstrap import Bootstrap
|
||||
from squirrelbattle.translations import Translator
|
||||
|
||||
if __name__ == "__main__":
|
||||
Bootstrap.run_game()
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("--makemessages", "-mm", action="store_true",
|
||||
help="Extract translatable strings")
|
||||
parser.add_argument("--compilemessages", "-cm", action="store_true",
|
||||
help="Compile translatable strings")
|
||||
|
||||
args = parser.parse_args(sys.argv[1:])
|
||||
if args.makemessages:
|
||||
Translator.makemessages()
|
||||
elif args.compilemessages:
|
||||
Translator.compilemessages()
|
||||
else:
|
||||
Bootstrap.run_game()
|
||||
|
26
setup.py
26
setup.py
@ -1,17 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
|
||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import subprocess
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
with open("README.md", "r") as f:
|
||||
long_description = f.read()
|
||||
|
||||
# Compile messages
|
||||
for language in ["de", "en", "fr"]:
|
||||
args = ["msgfmt", "--check-format",
|
||||
"-o", f"squirrelbattle/locale/{language}/LC_MESSAGES"
|
||||
"/squirrelbattle.mo",
|
||||
f"squirrelbattle/locale/{language}/LC_MESSAGES"
|
||||
"/squirrelbattle.po"]
|
||||
print(f"Compiling {language} messages...")
|
||||
subprocess.Popen(args)
|
||||
|
||||
setup(
|
||||
name="squirrel-battle",
|
||||
version="3.14",
|
||||
author="ynerant",
|
||||
author_email="ynerant@crans.org",
|
||||
description="Watch out for squirrel's knifes!",
|
||||
version="3.14.1",
|
||||
author="ÿnérant, eichhornchen, nicomarg, charlse",
|
||||
author_email="squirrel-battle@crans.org",
|
||||
description="Watch out for squirrel's knives!",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://gitlab.crans.org/ynerant/squirrel-battle",
|
||||
@ -32,7 +46,7 @@ setup(
|
||||
],
|
||||
python_requires='>=3.6',
|
||||
include_package_data=True,
|
||||
package_data={"squirrelbattle": ["assets/*"]},
|
||||
package_data={"squirrelbattle": ["assets/*", "locale/*/*/*.mo"]},
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"squirrel-battle = squirrelbattle.bootstrap:Bootstrap.run_game",
|
||||
|
@ -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()
|
Loading…
Reference in New Issue
Block a user