Merge remote branch
This commit is contained in:
commit
ca67d5d7f4
4
COPYING
4
COPYING
|
@ -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.
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
Squirrel Battle
|
Squirrel Battle
|
||||||
Copyright (C) 2020 ÿnérant, eichhornchen, nicomarg, charlse
|
Copyright (C) 2020-2021 ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
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
|
If the program does terminal interaction, make it output a short
|
||||||
notice like this when it starts in an interactive mode:
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
Squirrel Battle Copyright (C) 2020 ÿnérant, eichhornchen, nicomarg, charlse
|
Squirrel Battle Copyright (C) 2020-2021 ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions; type `show c' for details.
|
under certain conditions; type `show c' for details.
|
||||||
|
|
59
README.md
59
README.md
|
@ -9,8 +9,63 @@
|
||||||
|
|
||||||
# Squirrel Battle
|
# Squirrel Battle
|
||||||
|
|
||||||
Attention aux couteaux des écureuils !
|
Squirrel Battle is an infinite rogue-like game with randomly generated levels, in which the player controls a squirrel in its quest down in a dungeon, using diverse items to defeat monsters, and trying not to die.
|
||||||
|
|
||||||
|
##Installation
|
||||||
|
|
||||||
|
####Via PyPI :
|
||||||
|
``` pip install --user squirrel-battle
|
||||||
|
``` to install
|
||||||
|
|
||||||
|
``` pip install --user --upgrade squirrel-battle
|
||||||
|
``` to upgrade
|
||||||
|
|
||||||
|
####Via ArchLinux package :
|
||||||
|
Download one of these two packages on the AUR :
|
||||||
|
|
||||||
|
* python-squirrel-battle
|
||||||
|
* python-squirrel-battle-git
|
||||||
|
|
||||||
|
####Via Debian package :
|
||||||
|
Available on our git repository, has a dependency on fonts-noto-color-emoji (to be found in the official Debian repositories).
|
||||||
|
|
||||||
|
Run ```
|
||||||
|
dpkg -i python3-squirrelbattle_3.14.1_all.deb
|
||||||
|
``` after downloading
|
||||||
|
|
||||||
|
In all cases, execute via command line : ```squirrel-battle```
|
||||||
|
|
||||||
|
##For first-time players
|
||||||
|
|
||||||
|
The game is played in a terminal only, preferably one that supports color, markdown and emojis, but it can be played with only grey levels and relatively classic unicode characters.
|
||||||
|
|
||||||
|
Upon starting, the game will display the main menu. To navigate in menus, use zqsd or the keyboard arrows. To validate one of the options, use the Enter key. Mouse click is also supported in most menus, **but not in game**.
|
||||||
|
|
||||||
|
The game in itself can have two types of display : using ascii and simple unicode characters, or using emojis. To activate emoji mode, go to the settings menu and select the squirrel texture pack. Emojis will not work if the terminal does not support them, so do tests before to ensure the terminal can display them.
|
||||||
|
|
||||||
|
The game is translated (almost entirely) in English, French, German and Spanish. To change the language, go to the settings menu.
|
||||||
|
|
||||||
|
Controls in-game are pretty basic : use zqsd or the keyboard arrows to move. To hit an ennemy, simply go in its direction if it is in an adjacent tile.
|
||||||
|
|
||||||
|
There are several special control keys, they can be changed in the settings menu :
|
||||||
|
|
||||||
|
* To close a store menu or go back to the main menu, use Space
|
||||||
|
* To open/close the inventory, use i
|
||||||
|
* To use an object in the inventory, use u
|
||||||
|
* To equip an object in the inventory, use e
|
||||||
|
* To use a long range weapon after it has been equipped, use l and then select the direction to shoot in
|
||||||
|
* To drop an object from the inventory, use r (to pick up an object, simply go on its tile, its automatic)
|
||||||
|
* To talk to certains entities (or open a chest), use t and then select the direction of the entity
|
||||||
|
* To wait a turn (rather than moving), use w
|
||||||
|
* To dance and confuse the ennemies, use y
|
||||||
|
* To use a ladder, use <
|
||||||
|
|
||||||
|
The dungeon consists in empty tiles (you can not go there), walls (which you can not cross) and floor ( :) ). Entities that move are usually monsters, but if you see a trumpet (or a '/'), do not kill it ! It is a familiar that will help you defeat monsters. Entities that do not move are either entities to which you can talk, like merchants and ... chests for some reason, or objects. Differentiating the two is not difficult, trying to go on the same tile as a living entity (or a chest) is impossible. Objects have pretty clear names, so it should not be too difficult determining what they do (if you still don't know, you can either read the docs, or test for yourself (beware of surprises though))
|
||||||
|
|
||||||
|
And that is all you need to get started! You can now start your adventure and don't worry, floors are randomly generated, so it won't always be the same boring level.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
La documentation du projet est présente sur [squirrel-battle.readthedocs.io](https://squirrel-battle.readthedocs.io).
|
The documentation for the project cen be found at [squirrel-battle.readthedocs.io](https://squirrel-battle.readthedocs.io). It is unfortunately only written in French.
|
||||||
|
|
||||||
|
Anyone interested in understanding how the code works can find a few explanations in the documentation.
|
||||||
|
|
15
docs/conf.py
15
docs/conf.py
|
@ -18,8 +18,11 @@
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'Squirrel Battle'
|
project = 'Squirrel Battle'
|
||||||
copyright = "2020"
|
copyright = "2020-2021"
|
||||||
author = "Yohann D'ANELLO,\nMathilde DEPRES,\nNicolas MARGULIES,\nCharles PEYRAT"
|
author = "Yohann D'ANELLO,\n" \
|
||||||
|
"Mathilde DEPRES,\n" \
|
||||||
|
"Nicolas MARGULIES,\n" \
|
||||||
|
"Charles PEYRAT"
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
@ -58,3 +61,11 @@ html_theme = 'sphinx_rtd_theme'
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['_static']
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
html_context = {
|
||||||
|
'gitlab_user': 'ynerant',
|
||||||
|
'gitlab_repo': 'squirrel-battle',
|
||||||
|
'gitlab_host': 'gitlab.crans.org',
|
||||||
|
'gitlab_version': 'master',
|
||||||
|
'display_gitlab': True,
|
||||||
|
}
|
||||||
|
|
|
@ -3,13 +3,6 @@ Gestion de l'affichage
|
||||||
|
|
||||||
.. _curses: https://docs.python.org/3/howto/curses.html
|
.. _curses: https://docs.python.org/3/howto/curses.html
|
||||||
|
|
||||||
L'intégralité de l'affichage du jeu est géré grâce à la bibliothèque native de
|
|
||||||
Python curses_.
|
|
||||||
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
Plus de documentation à venir.
|
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
|
@ -19,3 +12,51 @@ Python curses_.
|
||||||
map
|
map
|
||||||
stats
|
stats
|
||||||
logs
|
logs
|
||||||
|
|
||||||
|
L'intégralité de l'affichage du jeu est géré grâce à la bibliothèque native de Python curses_.
|
||||||
|
|
||||||
|
Initialisation du terminal
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Au lancement du jeu, le terminal est initialisé, les caractères spéciaux sont traduits en abstraction curses, les
|
||||||
|
caractères tapés par l'utilisateur ne sont plus affichés sur le terminal, on envoie les touches tapées à curses en
|
||||||
|
permanence sans avoir à taper sur Entrée, le curseur est rendu invisible, on active le support des couleurs et enfin
|
||||||
|
on déclare vouloir attraper tous les clics de souris. Tout cela est fait dans un contexte Python, qui permet
|
||||||
|
d'effectuer les opérations inverses lors de la fermeture du programme, même en cas de crash, afin de retrouver un
|
||||||
|
terminal utilisable.
|
||||||
|
|
||||||
|
|
||||||
|
Pads
|
||||||
|
----
|
||||||
|
|
||||||
|
Chaque morceau d'affichage est géré dans un *pad*. Un pad est un objet défini par curses, représentant une sous-fenêtre,
|
||||||
|
qui a l'avantage d'être un peu plus flexible qu'une simple fenêtre. Un pad peut en effet dépasser de l'écran, et on
|
||||||
|
peut choisir où placer le coin avant gauche et quelle partie du pad il faut dessiner sur la fenêtre.
|
||||||
|
|
||||||
|
Ce projet implémente une couche d'abstraction supplémentaire, afin d'avoir des objets plus utilisables. Chaque partie
|
||||||
|
d'affichage est réprésentée dans une classé étendant ``Display``. Chaque ``Display`` contient un (ou plusieurs) pads,
|
||||||
|
et propose une surcouche de certaines fonctions de curses.
|
||||||
|
|
||||||
|
L'affichage de texte par exemple sur un pad est plus sécurisée que celle proposée par curses. Le comportement par défaut
|
||||||
|
est d'afficher un message à partir d'une position donnée avec des attributs (gras, couleur, ...) donnés sous
|
||||||
|
forme numérique. Cette implémentation permet de passer les attributs voulus sous forme de paramètres booléens, de
|
||||||
|
choisir la couleur de front/de fond sans définir de paire curses, mais surtout de tronquer le texte à la place
|
||||||
|
disponible, afin de ne pas avoir à se soucier d'avoir un message trop grand et éviter des crashs non désirés.
|
||||||
|
|
||||||
|
Les ``Display`` sont gérés par un ``DisplayManager``. C'est lui qui décide, en fonction de l'état actuel du jeu,
|
||||||
|
d'afficher les bons ``Display`` aux bons endroits et de les redimensionner correctement en fonction de la taille du
|
||||||
|
terminal. C'est aussi lui qui propage l'information de modifier les attributs d'un ``Display``, si par exemple
|
||||||
|
l'inventaire du joueur a été mis à jour.
|
||||||
|
|
||||||
|
Il s'occupe enfin de tout redimensionner si jamais le terminal a changé de taille, après une intervention
|
||||||
|
de l'utilisateur.
|
||||||
|
|
||||||
|
|
||||||
|
Interactions avec la souris
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Le jeu attrape les clics de souris. C'est le ``DisplayManager``, connaissant l'état du jeu et ce qui est affiché à
|
||||||
|
quel endroit, qui va chercher sur quel ``Display`` on a cliqué. L'information est propagée au bon ``Display``, en
|
||||||
|
adaptant les coordonnées.
|
||||||
|
|
||||||
|
Tout ``Display`` qui contient un menu procède de la même façon pour propager l'information au bon menu.
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
Affichage de l'historique
|
Affichage de l'historique
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
L'historique des actions est affiché en bas de l'écran. À chaque action d'une entité, comme frapper quelqu'un, ou lorsque le joueur parle à une entité, cela s'affiche dans l'historique.
|
L'historique des actions est affiché en bas de l'écran. À chaque action d'une entité, comme frapper quelqu'un,
|
||||||
|
ou lorsque le joueur parle à une entité, cela s'affiche dans l'historique.
|
||||||
|
|
||||||
|
Il est affiché sur l'écran de jeu, en bas à gauche, occupant 1/5 de la hauteur et 4/5 de la largeur.
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
Affichage de la carte
|
Affichage de la carte
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
La carte s'affiche dans la partie en haut à gauche de l'écran, sur la plus grande partie de l'écran. On affiche les tuiles une par une, selon le texture pack sélectionné. La map est actualisée à chaque action d'une entité.
|
La carte s'affiche dans la partie en haut à gauche de l'écran, sur la plus grande partie de l'écran.
|
||||||
|
On affiche les tuiles une par une. Selon le pack de textures utilisé, les tuiles prennent un ou deux caractères de large.
|
||||||
|
Selon la visibilité de la case en fonction de la position du joueur, la couleur de la case sera plus ou moins sombre,
|
||||||
|
voire masquée si le joueur n'a jamais vu la case. Les entités sont ensuite affichées, si elles sont dans le champ de
|
||||||
|
vision du joueur.
|
||||||
|
|
||||||
L'afffichage de la carte suit les déplacements du joueur.
|
La carte est actualisée lorsque cela est nécessaire, à chaque tick de jeu.
|
||||||
|
|
||||||
|
L'afffichage de la carte suit les déplacements du joueur, dans le sens où la caméra est toujours centrée sur lui.
|
||||||
|
La carte prend 4/5 de l'affichage aussi bien en largeur qu'en hauteur.
|
||||||
|
|
|
@ -2,13 +2,16 @@ Affichage des menus
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Les menus sont affichés dans une boîte. On peut naviguer dedans avec les flèches haut et bas,
|
Les menus sont affichés dans une boîte. On peut naviguer dedans avec les flèches haut et bas,
|
||||||
et valider avec la touche entrée.
|
et valider avec la touche entrée. Il est également possible d'intéragir avec la souris.
|
||||||
|
|
||||||
Il y a plusieurs menus dans le jeu :
|
Il y a plusieurs menus dans le jeu :
|
||||||
|
|
||||||
* Le main menu, qui s'affiche au lancement du jeu.
|
* **Le menu principal**, qui s'affiche au lancement du jeu.
|
||||||
* Le menu des paramètres : si on sélectionne un choix de touche et qu'on appuie sur entrée, on peut ensuite appuyer sur une touche pour remplacer la touche utilisée.
|
* **Le menu des paramètres** : si on sélectionne un choix de touche et qu'on appuie sur entrée,
|
||||||
* Le menu des crédits : ce menu fonctionne avec la souris. En cliquant on affiche une image.
|
on peut ensuite appuyer sur une touche pour remplacer la touche utilisée.
|
||||||
* Le menu d'inventaire : dans l'inventaire, on peut utiliser les touches pour utiliser un item ou l'équiper...
|
* **Le menu des crédits** : ce menu fonctionne avec la souris. En cliquant on affiche une image.
|
||||||
* Le menu de vente : on peut utiliser les touches gauche et droite pour switcher entre l'inventaire du joueur et celui du marchand.
|
* **Le menu d'inventaire** : dans l'inventaire, on peut utiliser les touches pour utiliser un item ou l'équiper...
|
||||||
* Menu des warnings : Pas vraiment un menu, mais affiche juste un message dans une petite boite pour prévenir le joueur que quelquechose ne va pas.
|
* **Le menu de vente** : on peut utiliser les touches gauche et droite pour switcher entre l'inventaire du joueur
|
||||||
|
et celui du marchand.
|
||||||
|
* **Menu des warnings** : Pas vraiment un menu, mais affiche juste un message dans une petite boite pour prévenir
|
||||||
|
le joueur que quelque chose ne va pas.
|
||||||
|
|
|
@ -4,7 +4,7 @@ Affichage des statistiques
|
||||||
.. _Hazel: ../index.html
|
.. _Hazel: ../index.html
|
||||||
|
|
||||||
Les statistiques du joueur sont affichées en haut à droite de l'écran
|
Les statistiques du joueur sont affichées en haut à droite de l'écran
|
||||||
et séparées du reste de l'affichage par une barre verticale.
|
et séparées du reste de l'affichage par une barre verticale, occupant 1/5 de la place horizontale.
|
||||||
|
|
||||||
Les informations affichées sont :
|
Les informations affichées sont :
|
||||||
|
|
||||||
|
@ -24,3 +24,8 @@ Les informations affichées sont :
|
||||||
* **Equipped helmet** - le casque porté par le joueur.
|
* **Equipped helmet** - le casque porté par le joueur.
|
||||||
* **Hazel** - le nombre d'Hazel_ que le joueur possède.
|
* **Hazel** - le nombre d'Hazel_ que le joueur possède.
|
||||||
* **Vous êtes mort** - Éventuellement, si le joueur est mort.
|
* **Vous êtes mort** - Éventuellement, si le joueur est mort.
|
||||||
|
|
||||||
|
Si le joueur possède un `monocle <../entities/items.html#monocle>`_, alors les statistiques d'une entité proche sont
|
||||||
|
également affichées dessous.
|
||||||
|
|
||||||
|
Des aides de jeu peuvent enfin être affichées en bas, telles que la touche sur laquelle il faut appuyer.
|
||||||
|
|
|
@ -42,13 +42,21 @@ En interagissant avec un marchand, il est possible de lui acheter et de lui
|
||||||
vendre différents objets contre des Hazels, la monnaie du jeu.
|
vendre différents objets contre des Hazels, la monnaie du jeu.
|
||||||
Les prix sont fixés :
|
Les prix sont fixés :
|
||||||
|
|
||||||
|
* Anneau de coup critique : 15 Hazels
|
||||||
|
* Anneau d'expérience : 25 Hazels
|
||||||
|
* Arc : 22 Hazels
|
||||||
|
* Baguette de feu : 36 Hazels
|
||||||
* Bombe : 4 Hazels
|
* Bombe : 4 Hazels
|
||||||
* Coeur : 3 Hazels
|
|
||||||
* Potion d'arrachage de corps : 14 Hazels
|
|
||||||
* Épée : 20 Hazels
|
|
||||||
* Bouclier : 16 Hazels
|
* Bouclier : 16 Hazels
|
||||||
* Casque : 18 Hazels
|
* Casque : 18 Hazels
|
||||||
|
* Coeur : 3 Hazels
|
||||||
|
* Épée : 20 Hazels
|
||||||
|
* Monocle : 10 Hazels
|
||||||
|
* Parchemin de dégâts : 18 Hazels
|
||||||
|
* Parchemin de faiblesse : 13 Hazels
|
||||||
* Plastron : 30 Hazels
|
* Plastron : 30 Hazels
|
||||||
|
* Potion d'arrachage de corps : 14 Hazels
|
||||||
|
* Règle : 2 Hazels
|
||||||
|
|
||||||
Le marchand commence avec 75 Hazels en sa possession, contre 42 pour le joueur.
|
Le marchand commence avec 75 Hazels en sa possession, contre 42 pour le joueur.
|
||||||
|
|
||||||
|
@ -59,11 +67,11 @@ Dans le `pack de textures`_ écureuil, il est représenté par l'émoji ``🦜``
|
||||||
Trompette
|
Trompette
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Son nom est fixé à 'trumpet'. Une trompette est un familier, c'est à dire que
|
Son nom est fixé à `trumpet`. Une trompette est un familier, c'est à dire que
|
||||||
c'est une entité attaquante qui suit globalement le joueurs et attaque les monstres
|
c'est une entité attaquante qui suit globalement le joueurs et attaque les monstres
|
||||||
qui se rapprochent trop du joueur.
|
qui se rapprochent trop du joueur.
|
||||||
|
|
||||||
Elle a 20 point de vie et une attaque de 3.
|
Elle a 20 points de vie et une attaque de 3.
|
||||||
|
|
||||||
Dans le `pack de textures`_ ASCII, elle est représentée par le caractère ``/``.
|
Dans le `pack de textures`_ ASCII, elle est représentée par le caractère ``/``.
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,10 @@ Bombe
|
||||||
|
|
||||||
Une bombe est un objet que l'on peut ramasser. Une fois ramassée, elle est placée
|
Une bombe est un objet que l'on peut ramasser. Une fois ramassée, elle est placée
|
||||||
dans l'inventaire. Le joueur peut ensuite utiliser la bombe, via l'inventaire
|
dans l'inventaire. Le joueur peut ensuite utiliser la bombe, via l'inventaire
|
||||||
ou après l'avoir équipée, qui fera alors 3 dégâts à toutes les
|
ou après l'avoir équipée, qui fera alors 5 dégâts à toutes les
|
||||||
`entités attaquantes`_ situées à moins de trois cases au bout de 4 ticks de jeu.
|
`entités attaquantes`_ situées à moins de trois cases au bout de 4 ticks de jeu.
|
||||||
|
|
||||||
Elle est représentée dans le `pack de textures`_ ASCII par le caractère ``o``
|
Elle est représentée dans le `pack de textures`_ ASCII par le caractère ``ç``
|
||||||
et dans le `pack de textures`_ écureuil par l'émoji ``💣``. Lors de l'explosion,
|
et dans le `pack de textures`_ écureuil par l'émoji ``💣``. Lors de l'explosion,
|
||||||
la bombe est remplacée par un symbole ``%`` ou l'émoji ``💥`` selon le pack de
|
la bombe est remplacée par un symbole ``%`` ou l'émoji ``💥`` selon le pack de
|
||||||
textures utilisé.
|
textures utilisé.
|
||||||
|
@ -44,8 +44,7 @@ Cœur
|
||||||
----
|
----
|
||||||
|
|
||||||
Un cœur est un objet que l'on ne peut pas ramasser. Dès que le joueur s'en
|
Un cœur est un objet que l'on ne peut pas ramasser. Dès que le joueur s'en
|
||||||
approche ou qu'il l'achète auprès d'un marchand, il est régénéré automatiquement
|
approche ou qu'il l'achète auprès d'un marchand, il récupère automatiquement 5 points de vie, et le cœur disparaît.
|
||||||
de 3 points de vie, et le cœur disparaît.
|
|
||||||
|
|
||||||
Il est représenté dans le `pack de textures`_ ASCII par le caractère ``❤``
|
Il est représenté dans le `pack de textures`_ ASCII par le caractère ``❤``
|
||||||
et dans le `pack de textures`_ écureuil par l'émoji ``💜``.
|
et dans le `pack de textures`_ écureuil par l'émoji ``💜``.
|
||||||
|
@ -65,11 +64,21 @@ Elle est représentée par les caractères ``I`` et ``🔀``
|
||||||
Cette potion coûte 14 Hazels auprès des marchands.
|
Cette potion coûte 14 Hazels auprès des marchands.
|
||||||
|
|
||||||
|
|
||||||
|
Règle
|
||||||
|
-----
|
||||||
|
|
||||||
|
La règle est une arme que l'on peut trouver uniquement par achat auprès d'un
|
||||||
|
marchand pour le coût de 2 Hazels ou dans un coffre. Une fois équipée, la règle ajoute 1 de force
|
||||||
|
à son porteur.
|
||||||
|
|
||||||
|
Elle est représentée par les caractères ``\`` et ``📏``.
|
||||||
|
|
||||||
|
|
||||||
Épée
|
Épée
|
||||||
----
|
----
|
||||||
|
|
||||||
L'épée est un objet que l'on peut trouver uniquement par achat auprès d'un
|
L'épée est une arme que l'on peut trouver uniquement par achat auprès d'un
|
||||||
marchand pour le coût de 20 Hazels. Une fois équipée, l'épée ajoute 3 de force
|
marchand pour le coût de 20 Hazels ou dans un coffre. Une fois équipée, l'épée ajoute 3 de force
|
||||||
à son porteur.
|
à son porteur.
|
||||||
|
|
||||||
Elle est représentée par les caractères ``†`` et ``🗡️``.
|
Elle est représentée par les caractères ``†`` et ``🗡️``.
|
||||||
|
@ -78,38 +87,34 @@ Elle est représentée par les caractères ``†`` et ``🗡️``.
|
||||||
Bouclier
|
Bouclier
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Le bouclier est un objet que l'on peut trouver uniquement par achat auprès d'un
|
Le bouclier est un type d'armure que l'on peut trouver uniquement par achat auprès d'un marchand pour le coût de 16 Hazels ou dans un coffre. Il s'équippe dans la main secondaire.
|
||||||
marchand pour le coût de 16 Hazels. Il s'équippe dans la main secondaire.
|
Une fois équipé, le bouclier ajoute 2 de constitution à son porteur, lui permettant de parer mieux les coups.
|
||||||
Une fois équipé, le bouclier ajoute 1 de
|
|
||||||
constitution à son porteur, lui permettant de parer mieux les coups.
|
|
||||||
|
|
||||||
Il est représenté par les caractères ``D`` et ``🛡️``.
|
Il est représenté par les caractères ``D`` et ``🛡️``.
|
||||||
|
|
||||||
Casque
|
Casque
|
||||||
------
|
------
|
||||||
|
|
||||||
Le casque est un objet que l'on peut trouver uniquement par achat auprès d'un
|
Le casque est un type d'armure que l'on peut trouver uniquement par achat auprès d'un marchand pour le coût de 18 Hazels ou dans un coffre. Il s'équippe sur la tête.
|
||||||
marchand pour le coût de 18 Hazels. Il s'équippe sur la tête.
|
Une fois équipé, le casque ajoute 2 de constitution à son porteur, lui permettant de prendre moins de dégâts.
|
||||||
Une fois équipé, le casque ajoute 2 de
|
|
||||||
constitution à son porteur, lui permettant de prendre moins de dêgats.
|
|
||||||
|
|
||||||
Il est représenté par les caractères ``0`` et ``⛑️``.
|
Il est représenté par les caractères ``0`` et ``⛑️``.
|
||||||
|
|
||||||
Plastron
|
Plastron
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Le plastron est un objet que l'on peut trouver uniquement par achat auprès d'un
|
Le plastron est un type d'armure que l'on peut trouver uniquement par achat
|
||||||
marchand pour le coût de 30 Hazels. Il s'équippe sur le corps.
|
auprès d'un marchand pour le coût de 30 Hazels ou dans un coffre. Il s'équippe sur le corps.
|
||||||
Une fois équipé, le casque ajoute 4 de
|
Une fois équipé, le casque ajoute 4 de constitution à son porteur,
|
||||||
constitution à son porteur, lui permettant de prendre moins de dêgats.
|
lui permettant de prendre moins de dégâts.
|
||||||
|
|
||||||
Il est représenté par les caractères ``(`` et ``🦺``.
|
Il est représenté par les caractères ``(`` et ``🦺``.
|
||||||
|
|
||||||
Anneau
|
Anneau
|
||||||
------
|
------
|
||||||
|
|
||||||
L'anneau est un objet que l'on peut trouver uniquement par achat auprès d'un
|
Un anneau est un objet que l'on peut trouver uniquement par achat auprès d'un
|
||||||
marchand. Il s'équippe sur la main secondaire.
|
marchand ou dans un coffre. Il s'équippe sur la main secondaire.
|
||||||
Une fois équipé, l'anneau ajoute un bonus à une ou plusieurs statistiques du
|
Une fois équipé, l'anneau ajoute un bonus à une ou plusieurs statistiques du
|
||||||
joueur, améliorant sa capacité à se débarasser des monstres.
|
joueur, améliorant sa capacité à se débarasser des monstres.
|
||||||
|
|
||||||
|
@ -119,3 +124,47 @@ Il y a plusieurs types d'anneaux :
|
||||||
* **Anneau de gain d'expérience amélioré**, qui multiplie le gain d'expérience du joueur par 2. Il coûte 25 Hazels.
|
* **Anneau de gain d'expérience amélioré**, qui multiplie le gain d'expérience du joueur par 2. Il coûte 25 Hazels.
|
||||||
|
|
||||||
Un anneau est représenté par les caractères ``o`` et ``💍``.
|
Un anneau est représenté par les caractères ``o`` et ``💍``.
|
||||||
|
|
||||||
|
Monocle
|
||||||
|
-------
|
||||||
|
|
||||||
|
L'anneau est un objet que l'on peut trouver uniquement par achat auprès d'un
|
||||||
|
marchand pour le prix de 10 Hazels. On peut le trouver sinon dans les coffres.
|
||||||
|
Il s'équippe sur la main secondaire.
|
||||||
|
|
||||||
|
Une fois porté, il permet de voir les caractéristiques des entités voisines
|
||||||
|
(nom, force, chance de critique, ...).
|
||||||
|
|
||||||
|
Un monocle est représenté par les caractères ``ô`` et ``🧐``.
|
||||||
|
|
||||||
|
Parchemin
|
||||||
|
---------
|
||||||
|
|
||||||
|
Un parchemin est un objet consommable qui se trouve chez un marchand ou dans un coffre. Lorsqu'il est utilisé, il a un effet sur les statistiques du joueur ou des autres entités combattantes. L'intensité de l'effet du parchemin dépend de l'intelligence du joueur.
|
||||||
|
|
||||||
|
Il y a plusieurs types de parchemins :
|
||||||
|
|
||||||
|
* **Parchemin de dégâts**, qui inflige des dégâts à toutes les entités combattantes qui sont à distance moins de 5 du joueur (ça touche aussi les familiers, mais pas le joueur). Le nombre de points de dégâts est directement l'intelligence du joueur. Il coute 18 Hazels.
|
||||||
|
* **Parchemin de faiblesse**, qui réduit la force de toutes les entités sauf le joueur de min(1, intelligence//2) pendant 3 tours du jeu. Il coûte 13 Hazels.
|
||||||
|
|
||||||
|
Un parchemin est représenté par les caractères ``]`` et ``📜``.
|
||||||
|
|
||||||
|
Arc
|
||||||
|
---
|
||||||
|
|
||||||
|
Un arc est une arme à distance qui s'équippe dans la main principale. Pour l'utiliser, il faut appuyer sur la touche de lancer (l de base) puis une touche de direction. Une flèche est alors tirée dans cette direction, et touche le premier ennemi qu'elle rencontre, s'il existe, sur les 3 premières cases dans cette direction.
|
||||||
|
|
||||||
|
La flèche fait 4 + dextérité du joueur dégâts.
|
||||||
|
L'arc coûte 22 Hazels chez un marchand. On peut le trouver sinon dans les coffres.
|
||||||
|
|
||||||
|
Il est représenté par les caractères ``)`` et ``🏹``.
|
||||||
|
|
||||||
|
Baton de boule de feu
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Un baton est une arme à distance qui s'équippe dans la main principale. Pour l'utiliser, il faut appuyer sur la touche de lancer (l de base) puis une touche de direction. Une boule de feu est alors tirée dans cette direction, et touche le premier ennemi qu'elle rencontre, s'il existe, sur les 4 premières cases dans cette direction. Lorsqu'un ennemi est touché, une explosion est affichée sur sa case.
|
||||||
|
|
||||||
|
La boule de feu fait 6 + intelligence du joueur dégâts.
|
||||||
|
Le baton coûte 36 Hazels chez un marchand. On peut le trouver sinon dans les coffres.
|
||||||
|
|
||||||
|
Il est représenté par les caractères ``:`` et ``🪄``.
|
||||||
|
|
|
@ -5,6 +5,9 @@ Joueur
|
||||||
.. _`paramètres`: ../settings.html
|
.. _`paramètres`: ../settings.html
|
||||||
.. _`pack de textures`: ../texture-pack.html
|
.. _`pack de textures`: ../texture-pack.html
|
||||||
.. _`objet`: items.html
|
.. _`objet`: items.html
|
||||||
|
.. _`parchemins`: items.html#Parchemin
|
||||||
|
.. _`batons` : items.html#Baton de boule de feu
|
||||||
|
.. _`arc` : items.html#Arc
|
||||||
|
|
||||||
Le joueur est une `entité attaquante`_, contrôlée par l'utilisateur humain.
|
Le joueur est une `entité attaquante`_, contrôlée par l'utilisateur humain.
|
||||||
|
|
||||||
|
@ -32,6 +35,10 @@ Déplacement
|
||||||
|
|
||||||
Selon les paramètres_, il est possible de bouger le joueur dans les 4 directions
|
Selon les paramètres_, il est possible de bouger le joueur dans les 4 directions
|
||||||
en appuyant sur ``z``, ``q``, ``s``, ``d`` ou sur les flèches directionnelles.
|
en appuyant sur ``z``, ``q``, ``s``, ``d`` ou sur les flèches directionnelles.
|
||||||
|
(ou sur d'autres touches selon ce qui est écrit dans le menu des paramètres)
|
||||||
|
|
||||||
|
Le joueur peut aussi ne rien faire pendant son tour, il suffit d'appuyer sur
|
||||||
|
la touche d'attente (``w`` de base).
|
||||||
|
|
||||||
Le joueur se retrouvera bloqué s'il avance contre un mur. Si il avance sur un
|
Le joueur se retrouvera bloqué s'il avance contre un mur. Si il avance sur un
|
||||||
objet_, alors il prend l'objet_ et avance sur la case.
|
objet_, alors il prend l'objet_ et avance sur la case.
|
||||||
|
@ -40,6 +47,25 @@ S'il rencontre une autre `entité attaquante`_, alors il frappe l'entité en
|
||||||
infligeant autant de dégâts qu'il n'a de force. À chaque fois qu'une entité est
|
infligeant autant de dégâts qu'il n'a de force. À chaque fois qu'une entité est
|
||||||
tuée, le joueur gagne aléatoirement entre 3 et 7 points d'expérience.
|
tuée, le joueur gagne aléatoirement entre 3 et 7 points d'expérience.
|
||||||
|
|
||||||
|
Outre se déplacer et attaquer, le joueur peut utiliser la touche pour danser
|
||||||
|
(``y`` de base) durant son tour et danser. Selon son charisme, il a plus ou moins
|
||||||
|
de chances de rendre confus tous les ennemis à distance moins de 3. Un ennemi confus
|
||||||
|
ne peut pas attaquer.
|
||||||
|
|
||||||
|
|
||||||
|
Statistiques
|
||||||
|
------------
|
||||||
|
|
||||||
|
Le joueur possède plusieurs statistiques :
|
||||||
|
|
||||||
|
* Niveau : son niveau, qui dépend de combien d'expérience il a accumulé
|
||||||
|
* Expérience : la quantité d'expérience accumulée par le joueur, qui dépend de combien d'entités il a tué.
|
||||||
|
* Force : indique combien de dommages le joueur inflige à ses ennemis
|
||||||
|
* Intelligence : joue sur l'effet des objets magiques, tels que les `parchemins`_ ou les `batons`_
|
||||||
|
* Charisme : joue sur l'efficacité de la danse du joueur
|
||||||
|
* Dextérité : joue sur l'efficacité de l'`arc`_
|
||||||
|
* Constitution : joue sur la quantité de dégâts que le joueur prend lorsqu'un monstre le frappe
|
||||||
|
* Taux de critique : la chance (en pourcentage) que le joueur a de faire un coup critique
|
||||||
|
|
||||||
Expérience
|
Expérience
|
||||||
----------
|
----------
|
||||||
|
@ -49,4 +75,4 @@ Lorsque le joueur atteint la quantité d'expérience requise pour monter de nive
|
||||||
le joueur gagne un niveau, regagne toute sa vie, consomme son expérience et la
|
le joueur gagne un niveau, regagne toute sa vie, consomme son expérience et la
|
||||||
nouvelle quantité d'expérience requise est 10 fois le niveau actuel. De plus,
|
nouvelle quantité d'expérience requise est 10 fois le niveau actuel. De plus,
|
||||||
entre 5 et 10 fois le niveau actuel entités apparaissent aléatoirement sur la
|
entre 5 et 10 fois le niveau actuel entités apparaissent aléatoirement sur la
|
||||||
carte à la montée de niveau. Enfin, le joueur gagne en force en montant de niveau.
|
carte à la montée de niveau. Enfin, le joueur améliore ses statistiques en augmentant de niveau. Toutes les caractéristiques ne sont pas incrémentées à chaque niveau gagné.
|
||||||
|
|
14
docs/map.rst
14
docs/map.rst
|
@ -44,3 +44,17 @@ Mur
|
||||||
Les murs délimitent les salles du donjon. Personne ne peut les traverser.
|
Les murs délimitent les salles du donjon. Personne ne peut les traverser.
|
||||||
Ils sont représentés par un dièse ``#`` dans le `pack de textures`_ ASCII et
|
Ils sont représentés par un dièse ``#`` dans le `pack de textures`_ ASCII et
|
||||||
par une brique carrée ``🧱`` dans le `pack de textures`_ écureuil.
|
par une brique carrée ``🧱`` dans le `pack de textures`_ écureuil.
|
||||||
|
|
||||||
|
|
||||||
|
Échelles
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Les échelles sont les débuts et fin de niveau. Elles permettent de changer
|
||||||
|
d'étage en appuyant sur une touche. Elles sont représentées par un ``H`` dans
|
||||||
|
le `pack de textures`_ ASCII et par un émoji échelle ``🪜`` dans le
|
||||||
|
`pack de textures`_ écureuil.
|
||||||
|
|
||||||
|
Lorsqu'on est sur l'échelle du début de niveau, appuyer sur ``<`` permet de
|
||||||
|
monter d'un étage (revenir au niveau précédent). Lorsqu'on est sur l'échelle
|
||||||
|
de fin de niveau, on génère une nouvelle carte si ce n'est pas déjà fait avec
|
||||||
|
des monstres plus forts, et on place le joueur sur cette nouvelle carte.
|
||||||
|
|
|
@ -11,8 +11,9 @@ prêt à tout pour s'en sortir. Sa vision de rongeur lui permet d'observer
|
||||||
l'intégralité de la carte_, et à l'aide d'objets_, il va pouvoir affronter
|
l'intégralité de la carte_, et à l'aide d'objets_, il va pouvoir affronter
|
||||||
les monstres_ présents dans le donjon et gagner en expérience et en force.
|
les monstres_ présents dans le donjon et gagner en expérience et en force.
|
||||||
|
|
||||||
Le jeu fonctionne par niveau. À chaque niveau ``n`` du joueur, entre ``3n`` et
|
Le jeu fonctionne par étage. À chaque étage, différents monstres sont présents,
|
||||||
``7n`` entités apparaissent aléatoirement sur la carte.
|
et à l'aide d'objets, il pourra progresser dans le donjon et descendre de plus
|
||||||
|
en plus bas.
|
||||||
|
|
||||||
En tuant des ennemis, ce qu'il parvient à faire en fonçant directement sur eux
|
En tuant des ennemis, ce qu'il parvient à faire en fonçant directement sur eux
|
||||||
ayant mangé trop de noisettes (ou étant armé d'un couteau), l'écureuil va
|
ayant mangé trop de noisettes (ou étant armé d'un couteau), l'écureuil va
|
||||||
|
|
|
@ -27,6 +27,9 @@ Les touches utilisées de base sont :
|
||||||
* **Lacher un objet** : r
|
* **Lacher un objet** : r
|
||||||
* **Parler** : t
|
* **Parler** : t
|
||||||
* **Attendre** : w
|
* **Attendre** : w
|
||||||
|
* **Utiliser une arme à distance** : l
|
||||||
|
* **Dancer** : y
|
||||||
|
* **Utiliser une échelle** : <
|
||||||
|
|
||||||
Autres
|
Autres
|
||||||
------
|
------
|
||||||
|
|
|
@ -1,12 +1,50 @@
|
||||||
Exécution des tests
|
Exécution des tests
|
||||||
===================
|
===================
|
||||||
|
|
||||||
.. note::
|
|
||||||
La documentation va être revue ici.
|
|
||||||
|
|
||||||
Les tests sont gérés par ``pytest`` dans le module ``squirrelbattle.tests``.
|
|
||||||
|
|
||||||
``tox`` est un outil permettant de configurer l'exécution des tests. Ainsi, après
|
``tox`` est un outil permettant de configurer l'exécution des tests. Ainsi, après
|
||||||
installation de tox dans votre environnement virtuel via ``pip install tox``,
|
installation de tox dans votre environnement virtuel via ``pip install tox``,
|
||||||
il vous suffit d'exécuter ``tox -e py3`` pour lancer les tests et ``tox -e linters``
|
il vous suffit d'exécuter ``tox -e py3`` pour lancer les tests et ``tox -e linters``
|
||||||
pour vérifier la syntaxe du code.
|
pour vérifier la syntaxe du code.
|
||||||
|
|
||||||
|
Tests unitaires
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Les tests sont gérés par ``pytest`` dans le module ``squirrelbattle.tests``.
|
||||||
|
Le module ``pytest-cov`` permet de mesurer la couverture du code.
|
||||||
|
Pour lancer les tests, il suffit de lancer ``tox -e py3`` ou de manière équivalente
|
||||||
|
``pytest --cov=squirrelbattle/ --cov-report=term-missing squirrelbattle/``
|
||||||
|
|
||||||
|
L'intégration continue lance les tests pour les versions de Python de 3.6 à 3.10,
|
||||||
|
sur une distribution Alpine Linux.
|
||||||
|
|
||||||
|
Chaque partie du code est testée unitairement, pour obtenir une couverture
|
||||||
|
maximale et assurer un bon fonctionnement. En particulier, le jeu est lancé
|
||||||
|
en commençant sur une carte déterministe (non générée aléatoirement) chargée
|
||||||
|
depuis ``assets/example_map.txt``, sur laquelle sont placés des ennemis et objets
|
||||||
|
avec lesquels le joueur doit interagir. On vérifie qu'à chaque touche appuyée,
|
||||||
|
il se passe le bon comportement. Le comportement des différents menus est
|
||||||
|
également testé.
|
||||||
|
|
||||||
|
L'environnement de test ne disposant pas a priori d'un terminal, le jeu est
|
||||||
|
conçu pour fonctionner sans support graphique, avec un terminal fictif où les
|
||||||
|
primitives de curses sont implémentées pour ne rien faire. On ne peut alors
|
||||||
|
pas s'assurer du bon fonctionnement de curses.
|
||||||
|
|
||||||
|
De plus, une très fine partie du code est ignorée lors de la couverture, ce
|
||||||
|
qui correspond à la phase d'initialisation du terminal et à la boucle infinie
|
||||||
|
qui reçoit les touches de l'utilisateur, qu'il est alors impossible de tester
|
||||||
|
unitairement.
|
||||||
|
|
||||||
|
|
||||||
|
Analyseur syntaxique
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
``flake8`` est utilisé en guise d'analyseur syntaxique. Il vérifie si le code
|
||||||
|
est bien formatté afin d'assurer une certaine lisibilité. En particulier,
|
||||||
|
il vérifie l'indentation, si chaque variable est bien utilisée, s'il n'y a pas
|
||||||
|
d'import inutile et s'ils sont dans l'ordre lexicographique, si chaque ligne
|
||||||
|
fait au plus 80 caractères et si la signature de chaque fonction est bien
|
||||||
|
spécifiée.
|
||||||
|
|
||||||
|
Pour lancer l'analyse, ``tox -e linters`` suffit. L'intégration continue
|
||||||
|
effectue cette analyse à chaque commit.
|
||||||
|
|
2
main.py
2
main.py
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from squirrelbattle.game import Game
|
from .display.display_manager import DisplayManager
|
||||||
from squirrelbattle.display.display_manager import DisplayManager
|
from .game import Game
|
||||||
from squirrelbattle.term_manager import TermManager
|
from .term_manager import TermManager
|
||||||
|
|
||||||
|
|
||||||
class Bootstrap:
|
class Bootstrap:
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
import curses
|
|
||||||
|
|
||||||
from ..display.display import Box, Display
|
|
||||||
from ..game import Game
|
|
||||||
from ..resources import ResourceManager
|
|
||||||
from ..translations import gettext as _
|
|
||||||
|
|
||||||
|
|
||||||
class CreditsDisplay(Display):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.box = Box(*args, **kwargs)
|
|
||||||
self.pad = self.newpad(1, 1)
|
|
||||||
self.ascii_art_displayed = False
|
|
||||||
|
|
||||||
def update(self, game: Game) -> None:
|
|
||||||
return
|
|
||||||
|
|
||||||
def display(self) -> None:
|
|
||||||
self.box.refresh(self.y, self.x, self.height, self.width)
|
|
||||||
self.box.display()
|
|
||||||
self.pad.erase()
|
|
||||||
|
|
||||||
messages = [
|
|
||||||
_("Credits"),
|
|
||||||
"",
|
|
||||||
"Squirrel Battle",
|
|
||||||
"",
|
|
||||||
_("Developers:"),
|
|
||||||
"Yohann \"ÿnérant\" D'ANELLO",
|
|
||||||
"Mathilde \"eichhornchen\" DÉPRÉS",
|
|
||||||
"Nicolas \"nicomarg\" MARGULIES",
|
|
||||||
"Charles \"charsle\" PEYRAT",
|
|
||||||
"",
|
|
||||||
_("Translators:"),
|
|
||||||
"Hugo \"ifugao\" JACOB (español)",
|
|
||||||
]
|
|
||||||
|
|
||||||
for i, msg in enumerate(messages):
|
|
||||||
self.addstr(self.pad, i + (self.height - len(messages)) // 2,
|
|
||||||
(self.width - len(msg)) // 2, msg,
|
|
||||||
bold=(i == 0), italic=(":" in msg))
|
|
||||||
|
|
||||||
if self.ascii_art_displayed:
|
|
||||||
self.display_ascii_art()
|
|
||||||
|
|
||||||
self.refresh_pad(self.pad, 0, 0, self.y + 1, self.x + 1,
|
|
||||||
self.height + self.y - 2,
|
|
||||||
self.width + self.x - 2)
|
|
||||||
|
|
||||||
def display_ascii_art(self) -> None:
|
|
||||||
with open(ResourceManager.get_asset_path("ascii-art-ecureuil.txt"))\
|
|
||||||
as f:
|
|
||||||
ascii_art = f.read().split("\n")
|
|
||||||
|
|
||||||
height, width = len(ascii_art), len(ascii_art[0])
|
|
||||||
y_offset, x_offset = (self.height - height) // 2,\
|
|
||||||
(self.width - width) // 2
|
|
||||||
|
|
||||||
for i, line in enumerate(ascii_art):
|
|
||||||
for j, c in enumerate(line):
|
|
||||||
bg_color = curses.COLOR_WHITE
|
|
||||||
fg_color = curses.COLOR_BLACK
|
|
||||||
bold = False
|
|
||||||
if c == ' ':
|
|
||||||
bg_color = curses.COLOR_BLACK
|
|
||||||
elif c == '━' or c == '┃' or c == '⋀':
|
|
||||||
bold = True
|
|
||||||
fg_color = curses.COLOR_WHITE
|
|
||||||
bg_color = curses.COLOR_BLACK
|
|
||||||
elif c == '|':
|
|
||||||
bold = True # c = '┃'
|
|
||||||
fg_color = (100, 700, 1000)
|
|
||||||
bg_color = curses.COLOR_BLACK
|
|
||||||
elif c == '▓':
|
|
||||||
fg_color = (700, 300, 0)
|
|
||||||
elif c == '▒':
|
|
||||||
fg_color = (700, 300, 0)
|
|
||||||
bg_color = curses.COLOR_BLACK
|
|
||||||
elif c == '░':
|
|
||||||
fg_color = (350, 150, 0)
|
|
||||||
elif c == '█':
|
|
||||||
fg_color = (0, 0, 0)
|
|
||||||
bg_color = curses.COLOR_BLACK
|
|
||||||
elif c == '▬':
|
|
||||||
c = '█'
|
|
||||||
fg_color = (1000, 1000, 1000)
|
|
||||||
bg_color = curses.COLOR_BLACK
|
|
||||||
self.addstr(self.pad, y_offset + i, x_offset + j, c,
|
|
||||||
fg_color, bg_color, bold=bold)
|
|
||||||
|
|
||||||
def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
|
|
||||||
if self.pad.inch(y - 1, x - 1) != ord(" "):
|
|
||||||
self.ascii_art_displayed = True
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
|
@ -124,15 +124,24 @@ class Display:
|
||||||
return pad.addstr(y, x, msg, attr)
|
return pad.addstr(y, x, msg, attr)
|
||||||
|
|
||||||
def init_pair(self, number: int, foreground: int, background: int) -> None:
|
def init_pair(self, number: int, foreground: int, background: int) -> None:
|
||||||
|
foreground = foreground if self.screen and curses.can_change_color() \
|
||||||
|
and foreground < curses.COLORS \
|
||||||
|
else curses.COLOR_WHITE
|
||||||
|
background = background if self.screen and curses.can_change_color() \
|
||||||
|
and background < curses.COLORS \
|
||||||
|
else curses.COLOR_WHITE
|
||||||
return curses.init_pair(number, foreground, background) \
|
return curses.init_pair(number, foreground, background) \
|
||||||
if self.screen else None
|
if self.screen and curses.can_change_color() \
|
||||||
|
and number < curses.COLOR_PAIRS else None
|
||||||
|
|
||||||
def color_pair(self, number: int) -> int:
|
def color_pair(self, number: int) -> int:
|
||||||
return curses.color_pair(number) if self.screen else 0
|
return curses.color_pair(number) if self.screen \
|
||||||
|
and number < curses.COLOR_PAIRS else 0
|
||||||
|
|
||||||
def init_color(self, number: int, red: int, green: int, blue: int) -> None:
|
def init_color(self, number: int, red: int, green: int, blue: int) -> None:
|
||||||
return curses.init_color(number, red, green, blue) \
|
return curses.init_color(number, red, green, blue) \
|
||||||
if self.screen else None
|
if self.screen and curses.can_change_color() \
|
||||||
|
and number < curses.COLORS else None
|
||||||
|
|
||||||
def resize(self, y: int, x: int, height: int, width: int,
|
def resize(self, y: int, x: int, height: int, width: int,
|
||||||
resize_pad: bool = True) -> None:
|
resize_pad: bool = True) -> None:
|
||||||
|
@ -281,3 +290,29 @@ class Box(Display):
|
||||||
|
|
||||||
self.refresh_pad(self.pad, 0, 0, self.y, self.x,
|
self.refresh_pad(self.pad, 0, 0, self.y, self.x,
|
||||||
self.y + self.height - 1, self.x + self.width - 1)
|
self.y + self.height - 1, self.x + self.width - 1)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageDisplay(Display):
|
||||||
|
"""
|
||||||
|
A class to handle the display of popup messages.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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(self, game: Game) -> None:
|
||||||
|
self.message = game.message
|
||||||
|
|
||||||
|
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, bold=True)
|
||||||
|
self.refresh_pad(self.pad, 0, 0, self.y, self.x,
|
||||||
|
self.height + self.y - 1,
|
||||||
|
self.width + self.x - 1)
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
|
|
||||||
from squirrelbattle.display.creditsdisplay import CreditsDisplay
|
|
||||||
from squirrelbattle.display.display import VerticalSplit, HorizontalSplit, \
|
|
||||||
Display
|
|
||||||
from squirrelbattle.display.mapdisplay import MapDisplay
|
|
||||||
from squirrelbattle.display.messagedisplay import MessageDisplay
|
|
||||||
from squirrelbattle.display.statsdisplay import StatsDisplay
|
|
||||||
from squirrelbattle.display.menudisplay import MainMenuDisplay, \
|
|
||||||
PlayerInventoryDisplay, StoreInventoryDisplay, SettingsMenuDisplay
|
|
||||||
from squirrelbattle.display.logsdisplay import LogsDisplay
|
|
||||||
from squirrelbattle.display.texturepack import TexturePack
|
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
from squirrelbattle.game import Game, GameMode
|
|
||||||
from squirrelbattle.enums import DisplayActions
|
from .display import Display, HorizontalSplit, MessageDisplay, VerticalSplit
|
||||||
|
from .gamedisplay import LogsDisplay, MapDisplay, StatsDisplay
|
||||||
|
from .menudisplay import ChestInventoryDisplay, CreditsDisplay, \
|
||||||
|
MainMenuDisplay, PlayerInventoryDisplay, \
|
||||||
|
SettingsMenuDisplay, StoreInventoryDisplay
|
||||||
|
from .texturepack import TexturePack
|
||||||
|
from ..enums import DisplayActions
|
||||||
|
from ..game import Game, GameMode
|
||||||
|
|
||||||
|
|
||||||
class DisplayManager:
|
class DisplayManager:
|
||||||
|
@ -29,6 +25,7 @@ class DisplayManager:
|
||||||
self.logsdisplay = LogsDisplay(screen, pack)
|
self.logsdisplay = LogsDisplay(screen, pack)
|
||||||
self.playerinventorydisplay = PlayerInventoryDisplay(screen, pack)
|
self.playerinventorydisplay = PlayerInventoryDisplay(screen, pack)
|
||||||
self.storeinventorydisplay = StoreInventoryDisplay(screen, pack)
|
self.storeinventorydisplay = StoreInventoryDisplay(screen, pack)
|
||||||
|
self.chestinventorydisplay = ChestInventoryDisplay(screen, pack)
|
||||||
self.mainmenudisplay = MainMenuDisplay(self.game.main_menu,
|
self.mainmenudisplay = MainMenuDisplay(self.game.main_menu,
|
||||||
screen, pack)
|
screen, pack)
|
||||||
self.settingsmenudisplay = SettingsMenuDisplay(screen, pack)
|
self.settingsmenudisplay = SettingsMenuDisplay(screen, pack)
|
||||||
|
@ -40,7 +37,8 @@ class DisplayManager:
|
||||||
self.mainmenudisplay, self.settingsmenudisplay,
|
self.mainmenudisplay, self.settingsmenudisplay,
|
||||||
self.logsdisplay, self.messagedisplay,
|
self.logsdisplay, self.messagedisplay,
|
||||||
self.playerinventorydisplay,
|
self.playerinventorydisplay,
|
||||||
self.storeinventorydisplay, self.creditsdisplay]
|
self.storeinventorydisplay, self.creditsdisplay,
|
||||||
|
self.chestinventorydisplay]
|
||||||
self.update_game_components()
|
self.update_game_components()
|
||||||
|
|
||||||
def handle_display_action(self, action: DisplayActions, *params) -> None:
|
def handle_display_action(self, action: DisplayActions, *params) -> None:
|
||||||
|
@ -87,7 +85,8 @@ class DisplayManager:
|
||||||
|
|
||||||
if self.game.state == GameMode.PLAY \
|
if self.game.state == GameMode.PLAY \
|
||||||
or self.game.state == GameMode.INVENTORY \
|
or self.game.state == GameMode.INVENTORY \
|
||||||
or self.game.state == GameMode.STORE:
|
or self.game.state == GameMode.STORE\
|
||||||
|
or self.game.state == GameMode.CHEST:
|
||||||
# The map pad has already the good size
|
# The map pad has already the good size
|
||||||
self.mapdisplay.refresh(0, 0, self.rows * 4 // 5,
|
self.mapdisplay.refresh(0, 0, self.rows * 4 // 5,
|
||||||
self.mapdisplay.pack.tile_width
|
self.mapdisplay.pack.tile_width
|
||||||
|
@ -124,6 +123,19 @@ class DisplayManager:
|
||||||
pack.tile_width * (2 * self.cols // (5 * pack.tile_width)))
|
pack.tile_width * (2 * self.cols // (5 * pack.tile_width)))
|
||||||
displays.append(self.storeinventorydisplay)
|
displays.append(self.storeinventorydisplay)
|
||||||
displays.append(self.playerinventorydisplay)
|
displays.append(self.playerinventorydisplay)
|
||||||
|
elif self.game.state == GameMode.CHEST:
|
||||||
|
self.chestinventorydisplay.refresh(
|
||||||
|
self.rows // 10,
|
||||||
|
pack.tile_width * (self.cols // (2 * pack.tile_width)),
|
||||||
|
8 * self.rows // 10,
|
||||||
|
pack.tile_width * (2 * self.cols // (5 * pack.tile_width)))
|
||||||
|
self.playerinventorydisplay.refresh(
|
||||||
|
self.rows // 10,
|
||||||
|
pack.tile_width * (self.cols // (10 * pack.tile_width)),
|
||||||
|
8 * self.rows // 10,
|
||||||
|
pack.tile_width * (2 * self.cols // (5 * pack.tile_width)))
|
||||||
|
displays.append(self.chestinventorydisplay)
|
||||||
|
displays.append(self.playerinventorydisplay)
|
||||||
elif self.game.state == GameMode.MAINMENU:
|
elif self.game.state == GameMode.MAINMENU:
|
||||||
self.mainmenudisplay.refresh(0, 0, self.rows, self.cols)
|
self.mainmenudisplay.refresh(0, 0, self.rows, self.cols)
|
||||||
displays.append(self.mainmenudisplay)
|
displays.append(self.mainmenudisplay)
|
||||||
|
|
|
@ -1,14 +1,120 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
|
|
||||||
|
from .display import Display
|
||||||
from ..entities.items import Monocle
|
from ..entities.items import Monocle
|
||||||
from ..entities.player import Player
|
from ..entities.player import Player
|
||||||
from ..game import Game
|
from ..game import Game
|
||||||
from ..interfaces import FightingEntity
|
from ..interfaces import FightingEntity, Logs, Map
|
||||||
from ..translations import gettext as _
|
from ..translations import gettext as _
|
||||||
from .display import Display
|
|
||||||
|
|
||||||
|
class LogsDisplay(Display):
|
||||||
|
"""
|
||||||
|
A class to handle the display of the logs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logs: Logs
|
||||||
|
|
||||||
|
def __init__(self, *args) -> None:
|
||||||
|
super().__init__(*args)
|
||||||
|
self.pad = self.newpad(self.rows, self.cols)
|
||||||
|
|
||||||
|
def update(self, game: Game) -> None:
|
||||||
|
self.logs = game.logs
|
||||||
|
|
||||||
|
def display(self) -> None:
|
||||||
|
messages = self.logs.messages[-self.height:]
|
||||||
|
messages = messages[::-1]
|
||||||
|
self.pad.erase()
|
||||||
|
for i in range(min(self.height, len(messages))):
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
class MapDisplay(Display):
|
||||||
|
"""
|
||||||
|
A class to handle the display of the map.
|
||||||
|
"""
|
||||||
|
|
||||||
|
map: Map
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
|
||||||
|
def update(self, game: Game) -> None:
|
||||||
|
self.map = game.map
|
||||||
|
self.pad = self.newpad(self.map.height,
|
||||||
|
self.pack.tile_width * self.map.width + 1)
|
||||||
|
|
||||||
|
def update_pad(self) -> None:
|
||||||
|
for j in range(len(self.map.tiles)):
|
||||||
|
for i in range(len(self.map.tiles[j])):
|
||||||
|
if not self.map.seen_tiles[j][i]:
|
||||||
|
continue
|
||||||
|
fg, bg = self.map.tiles[j][i].visible_color(self.pack) if \
|
||||||
|
self.map.visibility[j][i] else \
|
||||||
|
self.map.tiles[j][i].hidden_color(self.pack)
|
||||||
|
self.addstr(self.pad, j, self.pack.tile_width * i,
|
||||||
|
self.map.tiles[j][i].char(self.pack), fg, bg)
|
||||||
|
for e in self.map.entities:
|
||||||
|
if self.map.visibility[e.y][e.x]:
|
||||||
|
self.addstr(self.pad, e.y, self.pack.tile_width * e.x,
|
||||||
|
self.pack[e.name.upper()],
|
||||||
|
self.pack.entity_fg_color,
|
||||||
|
self.pack.entity_bg_color)
|
||||||
|
|
||||||
|
# Display Path map for debug purposes
|
||||||
|
# from squirrelbattle.entities.player import Player
|
||||||
|
# players = [ p for p in self.map.entities if isinstance(p,Player) ]
|
||||||
|
# player = players[0] if len(players) > 0 else None
|
||||||
|
# if player:
|
||||||
|
# for x in range(self.map.width):
|
||||||
|
# for y in range(self.map.height):
|
||||||
|
# if (y,x) in player.paths:
|
||||||
|
# deltay, deltax = (y - player.paths[(y, x)][0],
|
||||||
|
# x - player.paths[(y, x)][1])
|
||||||
|
# if (deltay, deltax) == (-1, 0):
|
||||||
|
# character = '↓'
|
||||||
|
# elif (deltay, deltax) == (1, 0):
|
||||||
|
# character = '↑'
|
||||||
|
# elif (deltay, deltax) == (0, -1):
|
||||||
|
# character = '→'
|
||||||
|
# else:
|
||||||
|
# character = '←'
|
||||||
|
# self.addstr(self.pad, y, self.pack.tile_width * x,
|
||||||
|
# character, self.pack.tile_fg_color,
|
||||||
|
# self.pack.tile_bg_color)
|
||||||
|
|
||||||
|
def display(self) -> None:
|
||||||
|
y, x = self.map.currenty, self.pack.tile_width * self.map.currentx
|
||||||
|
deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1
|
||||||
|
pminrow, pmincol = y - deltay, x - deltax
|
||||||
|
sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0)
|
||||||
|
deltay, deltax = self.height - deltay, self.width - deltax
|
||||||
|
smaxrow = self.map.height - (y + deltay) + self.height - 1
|
||||||
|
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.erase()
|
||||||
|
self.update_pad()
|
||||||
|
self.refresh_pad(self.pad, pminrow, pmincol, sminrow, smincol, smaxrow,
|
||||||
|
smaxcol)
|
||||||
|
|
||||||
|
|
||||||
class StatsDisplay(Display):
|
class StatsDisplay(Display):
|
|
@ -1,31 +0,0 @@
|
||||||
# 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.game import Game
|
|
||||||
from squirrelbattle.interfaces import Logs
|
|
||||||
|
|
||||||
|
|
||||||
class LogsDisplay(Display):
|
|
||||||
"""
|
|
||||||
A class to handle the display of the logs.
|
|
||||||
"""
|
|
||||||
|
|
||||||
logs: Logs
|
|
||||||
|
|
||||||
def __init__(self, *args) -> None:
|
|
||||||
super().__init__(*args)
|
|
||||||
self.pad = self.newpad(self.rows, self.cols)
|
|
||||||
|
|
||||||
def update(self, game: Game) -> None:
|
|
||||||
self.logs = game.logs
|
|
||||||
|
|
||||||
def display(self) -> None:
|
|
||||||
messages = self.logs.messages[-self.height:]
|
|
||||||
messages = messages[::-1]
|
|
||||||
self.pad.erase()
|
|
||||||
for i in range(min(self.height, len(messages))):
|
|
||||||
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,87 +0,0 @@
|
||||||
# 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
|
|
||||||
from ..game import Game
|
|
||||||
|
|
||||||
|
|
||||||
class MapDisplay(Display):
|
|
||||||
"""
|
|
||||||
A class to handle the display of the map.
|
|
||||||
"""
|
|
||||||
|
|
||||||
map: Map
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super().__init__(*args)
|
|
||||||
|
|
||||||
def update(self, game: Game) -> None:
|
|
||||||
self.map = game.map
|
|
||||||
self.pad = self.newpad(self.map.height,
|
|
||||||
self.pack.tile_width * self.map.width + 1)
|
|
||||||
|
|
||||||
def update_pad(self) -> None:
|
|
||||||
for j in range(len(self.map.tiles)):
|
|
||||||
for i in range(len(self.map.tiles[j])):
|
|
||||||
if not self.map.seen_tiles[j][i]:
|
|
||||||
continue
|
|
||||||
fg, bg = self.map.tiles[j][i].visible_color(self.pack) if \
|
|
||||||
self.map.visibility[j][i] else \
|
|
||||||
self.map.tiles[j][i].hidden_color(self.pack)
|
|
||||||
self.addstr(self.pad, j, self.pack.tile_width * i,
|
|
||||||
self.map.tiles[j][i].char(self.pack), fg, bg)
|
|
||||||
for e in self.map.entities:
|
|
||||||
if self.map.visibility[e.y][e.x]:
|
|
||||||
self.addstr(self.pad, e.y, self.pack.tile_width * e.x,
|
|
||||||
self.pack[e.name.upper()],
|
|
||||||
self.pack.entity_fg_color,
|
|
||||||
self.pack.entity_bg_color)
|
|
||||||
|
|
||||||
# Display Path map for debug purposes
|
|
||||||
# from squirrelbattle.entities.player import Player
|
|
||||||
# players = [ p for p in self.map.entities if isinstance(p,Player) ]
|
|
||||||
# player = players[0] if len(players) > 0 else None
|
|
||||||
# if player:
|
|
||||||
# for x in range(self.map.width):
|
|
||||||
# for y in range(self.map.height):
|
|
||||||
# if (y,x) in player.paths:
|
|
||||||
# deltay, deltax = (y - player.paths[(y, x)][0],
|
|
||||||
# x - player.paths[(y, x)][1])
|
|
||||||
# if (deltay, deltax) == (-1, 0):
|
|
||||||
# character = '↓'
|
|
||||||
# elif (deltay, deltax) == (1, 0):
|
|
||||||
# character = '↑'
|
|
||||||
# elif (deltay, deltax) == (0, -1):
|
|
||||||
# character = '→'
|
|
||||||
# else:
|
|
||||||
# character = '←'
|
|
||||||
# self.addstr(self.pad, y, self.pack.tile_width * x,
|
|
||||||
# character, self.pack.tile_fg_color,
|
|
||||||
# self.pack.tile_bg_color)
|
|
||||||
|
|
||||||
def display(self) -> None:
|
|
||||||
y, x = self.map.currenty, self.pack.tile_width * self.map.currentx
|
|
||||||
deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1
|
|
||||||
pminrow, pmincol = y - deltay, x - deltax
|
|
||||||
sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0)
|
|
||||||
deltay, deltax = self.height - deltay, self.width - deltax
|
|
||||||
smaxrow = self.map.height - (y + deltay) + self.height - 1
|
|
||||||
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.erase()
|
|
||||||
self.update_pad()
|
|
||||||
self.refresh_pad(self.pad, pminrow, pmincol, sminrow, smincol, smaxrow,
|
|
||||||
smaxcol)
|
|
|
@ -1,15 +1,15 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
from random import randint
|
from random import randint
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from squirrelbattle.menus import Menu, MainMenu, SettingsMenu, StoreMenu
|
|
||||||
from .display import Box, Display
|
from .display import Box, Display
|
||||||
from ..entities.player import Player
|
from ..entities.player import Player
|
||||||
from ..enums import KeyValues, GameMode
|
from ..enums import GameMode, KeyValues
|
||||||
from ..game import Game
|
from ..game import Game
|
||||||
|
from ..menus import ChestMenu, MainMenu, Menu, SettingsMenu, StoreMenu
|
||||||
from ..resources import ResourceManager
|
from ..resources import ResourceManager
|
||||||
from ..translations import gettext as _
|
from ..translations import gettext as _
|
||||||
|
|
||||||
|
@ -104,7 +104,8 @@ class MainMenuDisplay(Display):
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
self.menu = menu
|
self.menu = menu
|
||||||
|
|
||||||
with open(ResourceManager.get_asset_path("ascii_art.txt"), "r") as file:
|
with open(ResourceManager.get_asset_path("ascii_art-title.txt"), "r")\
|
||||||
|
as file:
|
||||||
self.title = file.read().split("\n")
|
self.title = file.read().split("\n")
|
||||||
|
|
||||||
self.pad = self.newpad(max(self.rows, len(self.title) + 30),
|
self.pad = self.newpad(max(self.rows, len(self.title) + 30),
|
||||||
|
@ -156,13 +157,17 @@ class PlayerInventoryDisplay(MenuDisplay):
|
||||||
player: Player = None
|
player: Player = None
|
||||||
selected: bool = True
|
selected: bool = True
|
||||||
store_mode: bool = False
|
store_mode: bool = False
|
||||||
|
chest_mode: bool = False
|
||||||
|
|
||||||
def update(self, game: Game) -> None:
|
def update(self, game: Game) -> None:
|
||||||
self.player = game.player
|
self.player = game.player
|
||||||
self.update_menu(game.inventory_menu)
|
self.update_menu(game.inventory_menu)
|
||||||
|
game.inventory_menu.update_player(self.player)
|
||||||
self.store_mode = game.state == GameMode.STORE
|
self.store_mode = game.state == GameMode.STORE
|
||||||
|
self.chest_mode = game.state == GameMode.CHEST
|
||||||
self.selected = game.state == GameMode.INVENTORY \
|
self.selected = game.state == GameMode.INVENTORY \
|
||||||
or (self.store_mode and not game.is_in_store_menu)
|
or (self.store_mode and not game.is_in_store_menu)\
|
||||||
|
or (self.chest_mode and not game.is_in_chest_menu)
|
||||||
|
|
||||||
def update_pad(self) -> None:
|
def update_pad(self) -> None:
|
||||||
self.menubox.update_title(_("INVENTORY"))
|
self.menubox.update_title(_("INVENTORY"))
|
||||||
|
@ -241,3 +246,128 @@ class StoreInventoryDisplay(MenuDisplay):
|
||||||
self.menu.position = max(0, min(len(self.menu.values) - 1, y - 2))
|
self.menu.position = max(0, min(len(self.menu.values) - 1, y - 2))
|
||||||
game.is_in_store_menu = True
|
game.is_in_store_menu = True
|
||||||
game.handle_key_pressed(KeyValues.ENTER)
|
game.handle_key_pressed(KeyValues.ENTER)
|
||||||
|
|
||||||
|
|
||||||
|
class ChestInventoryDisplay(MenuDisplay):
|
||||||
|
"""
|
||||||
|
A class to handle the display of a merchant's inventory.
|
||||||
|
"""
|
||||||
|
menu: ChestMenu
|
||||||
|
selected: bool = False
|
||||||
|
|
||||||
|
def update(self, game: Game) -> None:
|
||||||
|
self.update_menu(game.chest_menu)
|
||||||
|
self.selected = game.is_in_chest_menu
|
||||||
|
|
||||||
|
def update_pad(self) -> None:
|
||||||
|
self.menubox.update_title(_("CHEST"))
|
||||||
|
for i, item in enumerate(self.menu.values):
|
||||||
|
rep = self.pack[item.name.upper()]
|
||||||
|
selection = f"[{rep}]" if i == self.menu.position \
|
||||||
|
and self.selected else f" {rep} "
|
||||||
|
self.addstr(self.pad, i + 1, 0, selection
|
||||||
|
+ " " + item.translated_name.capitalize())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def truewidth(self) -> int:
|
||||||
|
return max(1, self.height if hasattr(self, "height") else 10)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trueheight(self) -> int:
|
||||||
|
return 2 + super().trueheight
|
||||||
|
|
||||||
|
def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
|
||||||
|
"""
|
||||||
|
We can select a menu item with the mouse.
|
||||||
|
"""
|
||||||
|
self.menu.position = max(0, min(len(self.menu.values) - 1, y - 2))
|
||||||
|
game.is_in_chest_menu = True
|
||||||
|
game.handle_key_pressed(KeyValues.ENTER)
|
||||||
|
|
||||||
|
|
||||||
|
class CreditsDisplay(Display):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.box = Box(*args, **kwargs)
|
||||||
|
self.pad = self.newpad(1, 1)
|
||||||
|
self.ascii_art_displayed = False
|
||||||
|
|
||||||
|
def update(self, game: Game) -> None:
|
||||||
|
return
|
||||||
|
|
||||||
|
def display(self) -> None:
|
||||||
|
self.box.refresh(self.y, self.x, self.height, self.width)
|
||||||
|
self.box.display()
|
||||||
|
self.pad.erase()
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
_("Credits"),
|
||||||
|
"",
|
||||||
|
"Squirrel Battle",
|
||||||
|
"",
|
||||||
|
_("Developers:"),
|
||||||
|
"Yohann \"ÿnérant\" D'ANELLO",
|
||||||
|
"Mathilde \"eichhornchen\" DÉPRÉS",
|
||||||
|
"Nicolas \"nicomarg\" MARGULIES",
|
||||||
|
"Charles \"charsle\" PEYRAT",
|
||||||
|
"",
|
||||||
|
_("Translators:"),
|
||||||
|
"Hugo \"ifugao\" JACOB (español)",
|
||||||
|
]
|
||||||
|
|
||||||
|
for i, msg in enumerate(messages):
|
||||||
|
self.addstr(self.pad, i + (self.height - len(messages)) // 2,
|
||||||
|
(self.width - len(msg)) // 2, msg,
|
||||||
|
bold=(i == 0), italic=(":" in msg))
|
||||||
|
|
||||||
|
if self.ascii_art_displayed:
|
||||||
|
self.display_ascii_art()
|
||||||
|
|
||||||
|
self.refresh_pad(self.pad, 0, 0, self.y + 1, self.x + 1,
|
||||||
|
self.height + self.y - 2,
|
||||||
|
self.width + self.x - 2)
|
||||||
|
|
||||||
|
def display_ascii_art(self) -> None:
|
||||||
|
with open(ResourceManager.get_asset_path("ascii-art-ecureuil.txt"))\
|
||||||
|
as f:
|
||||||
|
ascii_art = f.read().split("\n")
|
||||||
|
|
||||||
|
height, width = len(ascii_art), len(ascii_art[0])
|
||||||
|
y_offset, x_offset = (self.height - height) // 2,\
|
||||||
|
(self.width - width) // 2
|
||||||
|
|
||||||
|
for i, line in enumerate(ascii_art):
|
||||||
|
for j, c in enumerate(line):
|
||||||
|
bg_color = curses.COLOR_WHITE
|
||||||
|
fg_color = curses.COLOR_BLACK
|
||||||
|
bold = False
|
||||||
|
if c == ' ':
|
||||||
|
bg_color = curses.COLOR_BLACK
|
||||||
|
elif c == '━' or c == '┃' or c == '⋀':
|
||||||
|
bold = True
|
||||||
|
fg_color = curses.COLOR_WHITE
|
||||||
|
bg_color = curses.COLOR_BLACK
|
||||||
|
elif c == '|':
|
||||||
|
bold = True # c = '┃'
|
||||||
|
fg_color = (100, 700, 1000)
|
||||||
|
bg_color = curses.COLOR_BLACK
|
||||||
|
elif c == '▓':
|
||||||
|
fg_color = (700, 300, 0)
|
||||||
|
elif c == '▒':
|
||||||
|
fg_color = (700, 300, 0)
|
||||||
|
bg_color = curses.COLOR_BLACK
|
||||||
|
elif c == '░':
|
||||||
|
fg_color = (350, 150, 0)
|
||||||
|
elif c == '█':
|
||||||
|
fg_color = (0, 0, 0)
|
||||||
|
bg_color = curses.COLOR_BLACK
|
||||||
|
elif c == '▬':
|
||||||
|
c = '█'
|
||||||
|
fg_color = (1000, 1000, 1000)
|
||||||
|
bg_color = curses.COLOR_BLACK
|
||||||
|
self.addstr(self.pad, y_offset + i, x_offset + j, c,
|
||||||
|
fg_color, bg_color, bold=bold)
|
||||||
|
|
||||||
|
def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
|
||||||
|
if self.pad.inch(y - 1, x - 1) != ord(" "):
|
||||||
|
self.ascii_art_displayed = True
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
# 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
|
|
||||||
from squirrelbattle.game import Game
|
|
||||||
|
|
||||||
|
|
||||||
class MessageDisplay(Display):
|
|
||||||
"""
|
|
||||||
A class to handle the display of popup messages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
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(self, game: Game) -> None:
|
|
||||||
self.message = game.message
|
|
||||||
|
|
||||||
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, bold=True)
|
|
||||||
self.refresh_pad(self.pad, 0, 0, self.y, self.x,
|
|
||||||
self.height + self.y - 1,
|
|
||||||
self.width + self.x - 1)
|
|
|
@ -1,8 +1,8 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
from typing import Any, Union, Tuple
|
from typing import Any, Tuple, Union
|
||||||
|
|
||||||
|
|
||||||
class TexturePack:
|
class TexturePack:
|
||||||
|
@ -21,9 +21,12 @@ class TexturePack:
|
||||||
|
|
||||||
BODY_SNATCH_POTION: str
|
BODY_SNATCH_POTION: str
|
||||||
BOMB: str
|
BOMB: str
|
||||||
|
BOW: str
|
||||||
|
CHEST: str
|
||||||
CHESTPLATE: str
|
CHESTPLATE: str
|
||||||
EAGLE: str
|
EAGLE: str
|
||||||
EMPTY: str
|
EMPTY: str
|
||||||
|
FIRE_BALL_STAFF: str
|
||||||
FLOOR: str
|
FLOOR: str
|
||||||
HAZELNUT: str
|
HAZELNUT: str
|
||||||
HEART: str
|
HEART: str
|
||||||
|
@ -34,6 +37,9 @@ class TexturePack:
|
||||||
RABBIT: str
|
RABBIT: str
|
||||||
RING_OF_CRITICAL_DAMAGE: str
|
RING_OF_CRITICAL_DAMAGE: str
|
||||||
RING_OF_MORE_EXPERIENCE: str
|
RING_OF_MORE_EXPERIENCE: str
|
||||||
|
RULER: str
|
||||||
|
SCROLL_OF_DAMAGE: str
|
||||||
|
SCROLL_OF_WEAKENING: str
|
||||||
SHIELD: str
|
SHIELD: str
|
||||||
SUNFLOWER: str
|
SUNFLOWER: str
|
||||||
SWORD: str
|
SWORD: str
|
||||||
|
@ -73,10 +79,13 @@ TexturePack.ASCII_PACK = TexturePack(
|
||||||
|
|
||||||
BODY_SNATCH_POTION='S',
|
BODY_SNATCH_POTION='S',
|
||||||
BOMB='ç',
|
BOMB='ç',
|
||||||
|
BOW=')',
|
||||||
|
CHEST='□',
|
||||||
CHESTPLATE='(',
|
CHESTPLATE='(',
|
||||||
EAGLE='µ',
|
EAGLE='µ',
|
||||||
EMPTY=' ',
|
EMPTY=' ',
|
||||||
EXPLOSION='%',
|
EXPLOSION='%',
|
||||||
|
FIRE_BALL_STAFF=':',
|
||||||
FLOOR='.',
|
FLOOR='.',
|
||||||
LADDER='H',
|
LADDER='H',
|
||||||
HAZELNUT='¤',
|
HAZELNUT='¤',
|
||||||
|
@ -89,6 +98,7 @@ TexturePack.ASCII_PACK = TexturePack(
|
||||||
RABBIT='Y',
|
RABBIT='Y',
|
||||||
RING_OF_CRITICAL_DAMAGE='o',
|
RING_OF_CRITICAL_DAMAGE='o',
|
||||||
RING_OF_MORE_EXPERIENCE='o',
|
RING_OF_MORE_EXPERIENCE='o',
|
||||||
|
RULER='\\',
|
||||||
SHIELD='D',
|
SHIELD='D',
|
||||||
SUNFLOWER='I',
|
SUNFLOWER='I',
|
||||||
SWORD='\u2020',
|
SWORD='\u2020',
|
||||||
|
@ -96,6 +106,8 @@ TexturePack.ASCII_PACK = TexturePack(
|
||||||
TIGER='n',
|
TIGER='n',
|
||||||
TRUMPET='/',
|
TRUMPET='/',
|
||||||
WALL='#',
|
WALL='#',
|
||||||
|
SCROLL_OF_DAMAGE=']',
|
||||||
|
SCROLL_OF_WEAKENING=']',
|
||||||
)
|
)
|
||||||
|
|
||||||
TexturePack.SQUIRREL_PACK = TexturePack(
|
TexturePack.SQUIRREL_PACK = TexturePack(
|
||||||
|
@ -109,10 +121,13 @@ TexturePack.SQUIRREL_PACK = TexturePack(
|
||||||
|
|
||||||
BODY_SNATCH_POTION='🔀',
|
BODY_SNATCH_POTION='🔀',
|
||||||
BOMB='💣',
|
BOMB='💣',
|
||||||
|
BOW='🏹',
|
||||||
|
CHEST='🧰',
|
||||||
CHESTPLATE='🦺',
|
CHESTPLATE='🦺',
|
||||||
EAGLE='🦅',
|
EAGLE='🦅',
|
||||||
EMPTY=' ',
|
EMPTY=' ',
|
||||||
EXPLOSION='💥',
|
EXPLOSION='💥',
|
||||||
|
FIRE_BALL_STAFF='🪄',
|
||||||
FLOOR='██',
|
FLOOR='██',
|
||||||
LADDER=('🪜', curses.COLOR_WHITE, (1000, 1000, 1000),
|
LADDER=('🪜', curses.COLOR_WHITE, (1000, 1000, 1000),
|
||||||
curses.COLOR_WHITE, (1000, 1000, 1000)),
|
curses.COLOR_WHITE, (1000, 1000, 1000)),
|
||||||
|
@ -126,6 +141,7 @@ TexturePack.SQUIRREL_PACK = TexturePack(
|
||||||
RABBIT='🐇',
|
RABBIT='🐇',
|
||||||
RING_OF_CRITICAL_DAMAGE='💍',
|
RING_OF_CRITICAL_DAMAGE='💍',
|
||||||
RING_OF_MORE_EXPERIENCE='💍',
|
RING_OF_MORE_EXPERIENCE='💍',
|
||||||
|
RULER='📏',
|
||||||
SHIELD='🛡️ ',
|
SHIELD='🛡️ ',
|
||||||
SUNFLOWER='🌻',
|
SUNFLOWER='🌻',
|
||||||
SWORD='🗡️ ',
|
SWORD='🗡️ ',
|
||||||
|
@ -133,4 +149,6 @@ TexturePack.SQUIRREL_PACK = TexturePack(
|
||||||
TIGER='🐅',
|
TIGER='🐅',
|
||||||
TRUMPET='🎺',
|
TRUMPET='🎺',
|
||||||
WALL='🧱',
|
WALL='🧱',
|
||||||
|
SCROLL_OF_DAMAGE='📜',
|
||||||
|
SCROLL_OF_WEAKENING='📜',
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
from ..interfaces import FriendlyEntity, InventoryHolder, Map, FightingEntity
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
from ..translations import gettext as _
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
from .player import Player
|
|
||||||
from .monsters import Monster
|
|
||||||
from .items import Item
|
|
||||||
from random import choice, shuffle
|
from random import choice, shuffle
|
||||||
|
|
||||||
|
from .items import Bomb, Item
|
||||||
|
from .monsters import Monster
|
||||||
|
from .player import Player
|
||||||
|
from ..interfaces import Entity, FightingEntity, FriendlyEntity, \
|
||||||
|
InventoryHolder, Map
|
||||||
|
from ..translations import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class Merchant(InventoryHolder, FriendlyEntity):
|
class Merchant(InventoryHolder, FriendlyEntity):
|
||||||
"""
|
"""
|
||||||
|
@ -17,11 +22,13 @@ class Merchant(InventoryHolder, FriendlyEntity):
|
||||||
return super().keys() + ["inventory", "hazel"]
|
return super().keys() + ["inventory", "hazel"]
|
||||||
|
|
||||||
def __init__(self, name: str = "merchant", inventory: list = None,
|
def __init__(self, name: str = "merchant", inventory: list = None,
|
||||||
hazel: int = 75, *args, **kwargs):
|
hazel: int = 75, maxhealth: int = 8, *args, **kwargs):
|
||||||
super().__init__(name=name, *args, **kwargs)
|
super().__init__(name=name, maxhealth=maxhealth, *args, **kwargs)
|
||||||
self.inventory = self.translate_inventory(inventory or [])
|
self.inventory = self.translate_inventory(inventory) \
|
||||||
|
if inventory is not None else None
|
||||||
self.hazel = hazel
|
self.hazel = hazel
|
||||||
if not self.inventory:
|
if self.inventory is None:
|
||||||
|
self.inventory = []
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
self.inventory.append(choice(Item.get_all_items())())
|
self.inventory.append(choice(Item.get_all_items())())
|
||||||
|
|
||||||
|
@ -39,11 +46,54 @@ class Merchant(InventoryHolder, FriendlyEntity):
|
||||||
self.hazel += hz
|
self.hazel += hz
|
||||||
|
|
||||||
|
|
||||||
|
class Chest(InventoryHolder, FriendlyEntity):
|
||||||
|
"""
|
||||||
|
A class of chest inanimate entities which contain objects.
|
||||||
|
"""
|
||||||
|
annihilated: bool
|
||||||
|
|
||||||
|
def __init__(self, name: str = "chest", inventory: list = None,
|
||||||
|
hazel: int = 0, *args, **kwargs):
|
||||||
|
super().__init__(name=name, *args, **kwargs)
|
||||||
|
self.hazel = hazel
|
||||||
|
self.inventory = self.translate_inventory(inventory) \
|
||||||
|
if inventory is not None else None
|
||||||
|
self.annihilated = False
|
||||||
|
if self.inventory is None:
|
||||||
|
self.inventory = []
|
||||||
|
for i in range(3):
|
||||||
|
self.inventory.append(choice(Item.get_all_items())())
|
||||||
|
|
||||||
|
def talk_to(self, player: Player) -> str:
|
||||||
|
"""
|
||||||
|
This function is used to open the chest's inventory in a menu,
|
||||||
|
and allows the player to take objects.
|
||||||
|
"""
|
||||||
|
return _("You have opened the chest")
|
||||||
|
|
||||||
|
def take_damage(self, attacker: Entity, amount: int) -> str:
|
||||||
|
"""
|
||||||
|
A chest is not living, it can not take damage
|
||||||
|
"""
|
||||||
|
if isinstance(attacker, Bomb):
|
||||||
|
self.die()
|
||||||
|
self.annihilated = True
|
||||||
|
return _("The chest exploded")
|
||||||
|
return _("It's not really effective")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dead(self) -> bool:
|
||||||
|
"""
|
||||||
|
Chest can not die
|
||||||
|
"""
|
||||||
|
return self.annihilated
|
||||||
|
|
||||||
|
|
||||||
class Sunflower(FriendlyEntity):
|
class Sunflower(FriendlyEntity):
|
||||||
"""
|
"""
|
||||||
A friendly sunflower.
|
A friendly sunflower.
|
||||||
"""
|
"""
|
||||||
def __init__(self, maxhealth: int = 15,
|
def __init__(self, maxhealth: int = 20,
|
||||||
*args, **kwargs) -> None:
|
*args, **kwargs) -> None:
|
||||||
super().__init__(name="sunflower", maxhealth=maxhealth, *args, **kwargs)
|
super().__init__(name="sunflower", maxhealth=maxhealth, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -123,6 +173,6 @@ class Trumpet(Familiar):
|
||||||
A class of familiars.
|
A class of familiars.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name: str = "trumpet", strength: int = 3,
|
def __init__(self, name: str = "trumpet", strength: int = 3,
|
||||||
maxhealth: int = 20, *args, **kwargs) -> None:
|
maxhealth: int = 30, *args, **kwargs) -> None:
|
||||||
super().__init__(name=name, strength=strength,
|
super().__init__(name=name, strength=strength,
|
||||||
maxhealth=maxhealth, *args, **kwargs)
|
maxhealth=maxhealth, *args, **kwargs)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from ..interfaces import Entity, FightingEntity, Map, InventoryHolder
|
from ..interfaces import Entity, FightingEntity, InventoryHolder, Map
|
||||||
from ..translations import gettext as _
|
from ..translations import gettext as _
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,6 +47,11 @@ class Item(Entity):
|
||||||
Indicates what should be done when the item is used.
|
Indicates what should be done when the item is used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def throw(self, direction: int) -> Any:
|
||||||
|
"""
|
||||||
|
Indicates what should be done when the item is thrown.
|
||||||
|
"""
|
||||||
|
|
||||||
def equip(self) -> None:
|
def equip(self) -> None:
|
||||||
"""
|
"""
|
||||||
Indicates what should be done when the item is equipped.
|
Indicates what should be done when the item is equipped.
|
||||||
|
@ -86,16 +91,22 @@ class Item(Entity):
|
||||||
"""
|
"""
|
||||||
Returns the list of all item classes.
|
Returns the list of all item classes.
|
||||||
"""
|
"""
|
||||||
return [BodySnatchPotion, Chestplate, Bomb, Heart, Helmet, Monocle,
|
return [BodySnatchPotion, Bomb, Bow, Chestplate, FireBallStaff,
|
||||||
Shield, Sword, RingCritical, RingXP]
|
Heart, Helmet, Monocle, ScrollofDamage, ScrollofWeakening,
|
||||||
|
Shield, Sword, RingCritical, RingXP, Ruler]
|
||||||
|
|
||||||
def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool:
|
def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder,
|
||||||
|
for_free: bool = False) -> bool:
|
||||||
"""
|
"""
|
||||||
Does all necessary actions when an object is to be sold.
|
Does all necessary actions when an object is to be sold.
|
||||||
Is overwritten by some classes that cannot exist in the player's
|
Is overwritten by some classes that cannot exist in the player's
|
||||||
inventory.
|
inventory.
|
||||||
"""
|
"""
|
||||||
if buyer.hazel >= self.price:
|
if for_free:
|
||||||
|
self.hold(buyer)
|
||||||
|
seller.remove_from_inventory(self)
|
||||||
|
return True
|
||||||
|
elif buyer.hazel >= self.price:
|
||||||
self.hold(buyer)
|
self.hold(buyer)
|
||||||
seller.remove_from_inventory(self)
|
seller.remove_from_inventory(self)
|
||||||
buyer.change_hazel_balance(-self.price)
|
buyer.change_hazel_balance(-self.price)
|
||||||
|
@ -266,6 +277,15 @@ class Sword(Weapon):
|
||||||
super().__init__(name=name, price=price, *args, **kwargs)
|
super().__init__(name=name, price=price, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class Ruler(Weapon):
|
||||||
|
"""
|
||||||
|
A basic weapon
|
||||||
|
"""
|
||||||
|
def __init__(self, name: str = "ruler", price: int = 2,
|
||||||
|
damage: int = 1, *args, **kwargs):
|
||||||
|
super().__init__(name=name, price=price, damage=damage, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Armor(Item):
|
class Armor(Item):
|
||||||
"""
|
"""
|
||||||
Class of items that increase the player's constitution.
|
Class of items that increase the player's constitution.
|
||||||
|
@ -455,6 +475,166 @@ class RingXP(Ring):
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ScrollofDamage(Item):
|
||||||
|
"""
|
||||||
|
A scroll that, when used, deals damage to all entities in a certain radius.
|
||||||
|
"""
|
||||||
|
def __init__(self, name: str = "scroll_of_damage", price: int = 18,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(name=name, price=price, *args, **kwargs)
|
||||||
|
|
||||||
|
def use(self) -> None:
|
||||||
|
"""
|
||||||
|
Find all entities within a radius of 5, and deal damage based on the
|
||||||
|
player's intelligence.
|
||||||
|
"""
|
||||||
|
for entity in self.held_by.map.entities:
|
||||||
|
if entity.is_fighting_entity() and not entity == self.held_by:
|
||||||
|
if entity.distance(self.held_by) <= 5:
|
||||||
|
self.held_by.map.logs.add_message(entity.take_damage(
|
||||||
|
self.held_by, self.held_by.intelligence))
|
||||||
|
self.held_by.inventory.remove(self)
|
||||||
|
|
||||||
|
|
||||||
|
class ScrollofWeakening(Item):
|
||||||
|
"""
|
||||||
|
A scroll that, when used, reduces the damage of the ennemies for 3 turns.
|
||||||
|
"""
|
||||||
|
def __init__(self, name: str = "scroll_of_weakening", price: int = 13,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(name=name, price=price, *args, **kwargs)
|
||||||
|
|
||||||
|
def use(self) -> None:
|
||||||
|
"""
|
||||||
|
Find all entities and reduce their damage.
|
||||||
|
"""
|
||||||
|
for entity in self.held_by.map.entities:
|
||||||
|
if entity.is_fighting_entity() and not entity == self.held_by:
|
||||||
|
entity.strength = entity.strength - \
|
||||||
|
max(1, self.held_by.intelligence // 2)
|
||||||
|
entity.effects.append(["strength",
|
||||||
|
-max(1, self.held_by.intelligence // 2),
|
||||||
|
3])
|
||||||
|
self.held_by.map.logs.add_message(
|
||||||
|
_(f"The ennemies have -{max(1, self.held_by.intelligence // 2)}"
|
||||||
|
+ "strength for 3 turns"))
|
||||||
|
self.held_by.inventory.remove(self)
|
||||||
|
|
||||||
|
|
||||||
|
class LongRangeWeapon(Weapon):
|
||||||
|
def __init__(self, damage: int = 4,
|
||||||
|
rang: int = 3, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.damage = damage
|
||||||
|
self.range = rang
|
||||||
|
|
||||||
|
def throw(self, direction: int) -> Any:
|
||||||
|
to_kill = None
|
||||||
|
for entity in self.held_by.map.entities:
|
||||||
|
if entity.is_fighting_entity():
|
||||||
|
if direction == 0 and self.held_by.x == entity.x \
|
||||||
|
and self.held_by.y - entity.y > 0 and \
|
||||||
|
self.held_by.y - entity.y <= self.range:
|
||||||
|
to_kill = entity
|
||||||
|
elif direction == 2 and self.held_by.x == entity.x \
|
||||||
|
and entity.y - self.held_by.y > 0 and \
|
||||||
|
entity.y - self.held_by.y <= self.range:
|
||||||
|
to_kill = entity
|
||||||
|
elif direction == 1 and self.held_by.y == entity.y \
|
||||||
|
and entity.x - self.held_by.x > 0 and \
|
||||||
|
entity.x - self.held_by.x <= self.range:
|
||||||
|
to_kill = entity
|
||||||
|
elif direction == 3 and self.held_by.y == entity.y \
|
||||||
|
and self.held_by.x - entity.x > 0 and \
|
||||||
|
self.held_by.x - entity.x <= self.range:
|
||||||
|
to_kill = entity
|
||||||
|
if to_kill:
|
||||||
|
line = _("{name}").format(name=to_kill.translated_name.capitalize()
|
||||||
|
) + self.string + " "\
|
||||||
|
+ to_kill.take_damage(
|
||||||
|
self.held_by, self.damage
|
||||||
|
+ getattr(self.held_by, self.stat))
|
||||||
|
self.held_by.map.logs.add_message(line)
|
||||||
|
return (to_kill.y, to_kill.x) if to_kill else None
|
||||||
|
|
||||||
|
def equip(self) -> None:
|
||||||
|
"""
|
||||||
|
Equip the weapon.
|
||||||
|
"""
|
||||||
|
self.held_by.remove_from_inventory(self)
|
||||||
|
self.held_by.equipped_main = self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stat(self) -> str:
|
||||||
|
"""
|
||||||
|
The stat that is used when using the object: dexterity for a bow
|
||||||
|
or intelligence for a magic staff.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def string(self) -> str:
|
||||||
|
"""
|
||||||
|
The string that is printed when we hit an ennemy.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Bow(LongRangeWeapon):
|
||||||
|
"""
|
||||||
|
A type of long range weapon that deals damage
|
||||||
|
based on the player's dexterity
|
||||||
|
"""
|
||||||
|
def __init__(self, name: str = "bow", price: int = 22, damage: int = 4,
|
||||||
|
rang: int = 3, *args, **kwargs):
|
||||||
|
super().__init__(name=name, price=price, damage=damage,
|
||||||
|
rang=rang, *args, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stat(self) -> str:
|
||||||
|
"""
|
||||||
|
Here it is dexterity
|
||||||
|
"""
|
||||||
|
return "dexterity"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def string(self) -> str:
|
||||||
|
return _(" is shot by an arrow.")
|
||||||
|
|
||||||
|
|
||||||
|
class FireBallStaff(LongRangeWeapon):
|
||||||
|
"""
|
||||||
|
A type of powerful long range weapon that deals damage
|
||||||
|
based on the player's intelligence
|
||||||
|
"""
|
||||||
|
def __init__(self, name: str = "fire_ball_staff", price: int = 36,
|
||||||
|
damage: int = 6, rang: int = 4, *args, **kwargs):
|
||||||
|
super().__init__(name=name, price=price, damage=damage,
|
||||||
|
rang=rang, *args, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stat(self) -> str:
|
||||||
|
"""
|
||||||
|
Here it is intelligence
|
||||||
|
"""
|
||||||
|
return "intelligence"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def string(self) -> str:
|
||||||
|
return _(" is shot by a fire ball.")
|
||||||
|
|
||||||
|
def throw(self, direction: int) -> Any:
|
||||||
|
"""
|
||||||
|
Adds an explosion animation when killing something.
|
||||||
|
"""
|
||||||
|
coord = super().throw(direction)
|
||||||
|
if coord:
|
||||||
|
y = coord[0]
|
||||||
|
x = coord[1]
|
||||||
|
|
||||||
|
explosion = Explosion(y=y, x=x)
|
||||||
|
self.held_by.map.add_entity(explosion)
|
||||||
|
return y, x
|
||||||
|
|
||||||
|
|
||||||
class Monocle(Item):
|
class Monocle(Item):
|
||||||
def __init__(self, name: str = "monocle", price: int = 10,
|
def __init__(self, name: str = "monocle", price: int = 10,
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from random import shuffle
|
from random import shuffle
|
||||||
|
@ -31,6 +31,7 @@ class Monster(FightingEntity):
|
||||||
By default, a monster will move randomly where it is possible
|
By default, a monster will move randomly where it is possible
|
||||||
If the player is closeby, the monster runs to the player.
|
If the player is closeby, the monster runs to the player.
|
||||||
"""
|
"""
|
||||||
|
super().act(m)
|
||||||
target = None
|
target = None
|
||||||
for entity in m.entities:
|
for entity in m.entities:
|
||||||
if self.distance_squared(entity) <= 25 and \
|
if self.distance_squared(entity) <= 25 and \
|
||||||
|
@ -42,7 +43,9 @@ class Monster(FightingEntity):
|
||||||
# that targets the player.
|
# that targets the player.
|
||||||
# If they can not move and are already close to the player,
|
# If they can not move and are already close to the player,
|
||||||
# they hit.
|
# they hit.
|
||||||
if target and (self.y, self.x) in target.paths:
|
if target and (self.y, self.x) in target.paths and \
|
||||||
|
self.map.is_visible_from(self.y, self.x,
|
||||||
|
target.y, target.x, 5):
|
||||||
# Moves to target player by choosing the best available path
|
# Moves to target player by choosing the best available path
|
||||||
for next_y, next_x in target.paths[(self.y, self.x)]:
|
for next_y, next_x in target.paths[(self.y, self.x)]:
|
||||||
moved = self.check_move(next_y, next_x, True)
|
moved = self.check_move(next_y, next_x, True)
|
||||||
|
@ -73,8 +76,8 @@ class Tiger(Monster):
|
||||||
"""
|
"""
|
||||||
A tiger monster.
|
A tiger monster.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name: str = "tiger", strength: int = 2,
|
def __init__(self, name: str = "tiger", strength: int = 5,
|
||||||
maxhealth: int = 20, *args, **kwargs) -> None:
|
maxhealth: int = 30, *args, **kwargs) -> None:
|
||||||
super().__init__(name=name, strength=strength,
|
super().__init__(name=name, strength=strength,
|
||||||
maxhealth=maxhealth, *args, **kwargs)
|
maxhealth=maxhealth, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -94,7 +97,7 @@ class Rabbit(Monster):
|
||||||
A rabbit monster.
|
A rabbit monster.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name: str = "rabbit", strength: int = 1,
|
def __init__(self, name: str = "rabbit", strength: int = 1,
|
||||||
maxhealth: int = 15, critical: int = 30,
|
maxhealth: int = 20, critical: int = 30,
|
||||||
*args, **kwargs) -> None:
|
*args, **kwargs) -> None:
|
||||||
super().__init__(name=name, strength=strength,
|
super().__init__(name=name, strength=strength,
|
||||||
maxhealth=maxhealth, critical=critical,
|
maxhealth=maxhealth, critical=critical,
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from math import log
|
||||||
from random import randint
|
from random import randint
|
||||||
from typing import Dict, Optional, Tuple
|
from typing import Dict, Optional, Tuple
|
||||||
|
|
||||||
from .items import Item
|
from .items import Item
|
||||||
from ..interfaces import FightingEntity, InventoryHolder
|
from ..interfaces import FightingEntity, InventoryHolder
|
||||||
|
from ..translations import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class Player(InventoryHolder, FightingEntity):
|
class Player(InventoryHolder, FightingEntity):
|
||||||
|
@ -61,6 +63,31 @@ class Player(InventoryHolder, FightingEntity):
|
||||||
self.recalculate_paths()
|
self.recalculate_paths()
|
||||||
self.map.compute_visibility(self.y, self.x, self.vision)
|
self.map.compute_visibility(self.y, self.x, self.vision)
|
||||||
|
|
||||||
|
def dance(self) -> None:
|
||||||
|
"""
|
||||||
|
Dancing has a certain probability or making ennemies unable
|
||||||
|
to fight for 3 turns. That probability depends on the player's
|
||||||
|
charisma.
|
||||||
|
"""
|
||||||
|
diceroll = randint(1, 10)
|
||||||
|
found = False
|
||||||
|
if diceroll <= self.charisma:
|
||||||
|
for entity in self.map.entities:
|
||||||
|
if entity.is_fighting_entity() and not entity == self \
|
||||||
|
and entity.distance(self) <= 3:
|
||||||
|
found = True
|
||||||
|
entity.confused = 1
|
||||||
|
entity.effects.append(["confused", 1, 3])
|
||||||
|
if found:
|
||||||
|
self.map.logs.add_message(_(
|
||||||
|
"It worked! Nearby ennemies will be confused for 3 turns."))
|
||||||
|
else:
|
||||||
|
self.map.logs.add_message(_(
|
||||||
|
"It worked, but there is no one nearby..."))
|
||||||
|
else:
|
||||||
|
self.map.logs.add_message(
|
||||||
|
_("The dance was not effective..."))
|
||||||
|
|
||||||
def level_up(self) -> None:
|
def level_up(self) -> None:
|
||||||
"""
|
"""
|
||||||
Add as many levels as possible to the player.
|
Add as many levels as possible to the player.
|
||||||
|
@ -69,11 +96,18 @@ class Player(InventoryHolder, FightingEntity):
|
||||||
self.level += 1
|
self.level += 1
|
||||||
self.current_xp -= self.max_xp
|
self.current_xp -= self.max_xp
|
||||||
self.max_xp = self.level * 10
|
self.max_xp = self.level * 10
|
||||||
|
self.maxhealth += int(2 * log(self.level) / log(2))
|
||||||
self.health = self.maxhealth
|
self.health = self.maxhealth
|
||||||
self.strength = self.strength + 1
|
self.strength = self.strength + 1
|
||||||
# TODO Remove it, that's only fun
|
if self.level % 3 == 0:
|
||||||
self.map.spawn_random_entities(randint(3 * self.level,
|
self.dexterity += 1
|
||||||
10 * self.level))
|
self.constitution += 1
|
||||||
|
if self.level % 4 == 0:
|
||||||
|
self.intelligence += 1
|
||||||
|
if self.level % 6 == 0:
|
||||||
|
self.charisma += 1
|
||||||
|
if self.level % 10 == 0 and self.critical < 95:
|
||||||
|
self.critical += (100 - self.charisma) // 30
|
||||||
|
|
||||||
def add_xp(self, xp: int) -> None:
|
def add_xp(self, xp: int) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from enum import Enum, auto
|
from enum import auto, Enum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from squirrelbattle.settings import Settings
|
from squirrelbattle.settings import Settings
|
||||||
|
@ -28,6 +28,7 @@ class GameMode(Enum):
|
||||||
SETTINGS = auto()
|
SETTINGS = auto()
|
||||||
INVENTORY = auto()
|
INVENTORY = auto()
|
||||||
STORE = auto()
|
STORE = auto()
|
||||||
|
CHEST = auto()
|
||||||
CREDITS = auto()
|
CREDITS = auto()
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,9 +49,12 @@ class KeyValues(Enum):
|
||||||
CHAT = auto()
|
CHAT = auto()
|
||||||
WAIT = auto()
|
WAIT = auto()
|
||||||
LADDER = auto()
|
LADDER = auto()
|
||||||
|
LAUNCH = auto()
|
||||||
|
DANCE = auto()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]:
|
def translate_key(key: str, settings: Settings) \
|
||||||
|
-> Optional["KeyValues"]: # noqa: C901
|
||||||
"""
|
"""
|
||||||
Translates the raw string key into an enum value that we can use.
|
Translates the raw string key into an enum value that we can use.
|
||||||
"""
|
"""
|
||||||
|
@ -84,4 +88,7 @@ class KeyValues(Enum):
|
||||||
return KeyValues.WAIT
|
return KeyValues.WAIT
|
||||||
elif key == settings.KEY_LADDER:
|
elif key == settings.KEY_LADDER:
|
||||||
return KeyValues.LADDER
|
return KeyValues.LADDER
|
||||||
return None
|
elif key == settings.KEY_LAUNCH:
|
||||||
|
return KeyValues.LAUNCH
|
||||||
|
elif key == settings.KEY_DANCE:
|
||||||
|
return KeyValues.DANCE
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from json import JSONDecodeError
|
|
||||||
from random import randint
|
|
||||||
from typing import Any, Optional, List
|
|
||||||
import curses
|
import curses
|
||||||
import json
|
import json
|
||||||
|
from json import JSONDecodeError
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
|
from . import menus
|
||||||
from .entities.player import Player
|
from .entities.player import Player
|
||||||
from .enums import GameMode, KeyValues, DisplayActions
|
from .enums import DisplayActions, GameMode, KeyValues
|
||||||
from .interfaces import Map, Logs
|
from .interfaces import Logs, Map
|
||||||
from .mapgeneration import broguelike
|
from .mapgeneration import broguelike
|
||||||
from .resources import ResourceManager
|
from .resources import ResourceManager
|
||||||
from .settings import Settings
|
from .settings import Settings
|
||||||
from . import menus
|
|
||||||
from .translations import gettext as _, Translator
|
from .translations import gettext as _, Translator
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,7 +35,9 @@ class Game:
|
||||||
"""
|
"""
|
||||||
self.state = GameMode.MAINMENU
|
self.state = GameMode.MAINMENU
|
||||||
self.waiting_for_friendly_key = False
|
self.waiting_for_friendly_key = False
|
||||||
|
self.waiting_for_launch_key = False
|
||||||
self.is_in_store_menu = True
|
self.is_in_store_menu = True
|
||||||
|
self.is_in_chest_menu = True
|
||||||
self.settings = Settings()
|
self.settings = Settings()
|
||||||
self.settings.load_settings()
|
self.settings.load_settings()
|
||||||
self.settings.write_settings()
|
self.settings.write_settings()
|
||||||
|
@ -46,6 +47,7 @@ class Game:
|
||||||
self.settings_menu.update_values(self.settings)
|
self.settings_menu.update_values(self.settings)
|
||||||
self.inventory_menu = menus.InventoryMenu()
|
self.inventory_menu = menus.InventoryMenu()
|
||||||
self.store_menu = menus.StoreMenu()
|
self.store_menu = menus.StoreMenu()
|
||||||
|
self.chest_menu = menus.ChestMenu()
|
||||||
self.logs = Logs()
|
self.logs = Logs()
|
||||||
self.message = None
|
self.message = None
|
||||||
|
|
||||||
|
@ -61,7 +63,6 @@ class Game:
|
||||||
self.player = Player()
|
self.player = Player()
|
||||||
self.map.add_entity(self.player)
|
self.map.add_entity(self.player)
|
||||||
self.player.move(self.map.start_y, self.map.start_x)
|
self.player.move(self.map.start_y, self.map.start_x)
|
||||||
self.map.spawn_random_entities(randint(3, 10))
|
|
||||||
self.inventory_menu.update_player(self.player)
|
self.inventory_menu.update_player(self.player)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -121,6 +122,9 @@ class Game:
|
||||||
if self.waiting_for_friendly_key:
|
if self.waiting_for_friendly_key:
|
||||||
# The player requested to talk with a friendly entity
|
# The player requested to talk with a friendly entity
|
||||||
self.handle_friendly_entity_chat(key)
|
self.handle_friendly_entity_chat(key)
|
||||||
|
elif self.waiting_for_launch_key:
|
||||||
|
# The player requested to launch
|
||||||
|
self.handle_launch(key)
|
||||||
else:
|
else:
|
||||||
self.handle_key_pressed_play(key)
|
self.handle_key_pressed_play(key)
|
||||||
elif self.state == GameMode.INVENTORY:
|
elif self.state == GameMode.INVENTORY:
|
||||||
|
@ -131,6 +135,8 @@ class Game:
|
||||||
self.settings_menu.handle_key_pressed(key, raw_key, self)
|
self.settings_menu.handle_key_pressed(key, raw_key, self)
|
||||||
elif self.state == GameMode.STORE:
|
elif self.state == GameMode.STORE:
|
||||||
self.handle_key_pressed_store(key)
|
self.handle_key_pressed_store(key)
|
||||||
|
elif self.state == GameMode.CHEST:
|
||||||
|
self.handle_key_pressed_chest(key)
|
||||||
elif self.state == GameMode.CREDITS:
|
elif self.state == GameMode.CREDITS:
|
||||||
self.state = GameMode.MAINMENU
|
self.state = GameMode.MAINMENU
|
||||||
self.display_actions(DisplayActions.REFRESH)
|
self.display_actions(DisplayActions.REFRESH)
|
||||||
|
@ -159,6 +165,9 @@ class Game:
|
||||||
self.player.equipped_main.use()
|
self.player.equipped_main.use()
|
||||||
if self.player.equipped_secondary:
|
if self.player.equipped_secondary:
|
||||||
self.player.equipped_secondary.use()
|
self.player.equipped_secondary.use()
|
||||||
|
elif key == KeyValues.LAUNCH:
|
||||||
|
# Wait for the direction to launch in
|
||||||
|
self.waiting_for_launch_key = True
|
||||||
elif key == KeyValues.SPACE:
|
elif key == KeyValues.SPACE:
|
||||||
self.state = GameMode.MAINMENU
|
self.state = GameMode.MAINMENU
|
||||||
elif key == KeyValues.CHAT:
|
elif key == KeyValues.CHAT:
|
||||||
|
@ -168,6 +177,9 @@ class Game:
|
||||||
self.map.tick(self.player)
|
self.map.tick(self.player)
|
||||||
elif key == KeyValues.LADDER:
|
elif key == KeyValues.LADDER:
|
||||||
self.handle_ladder()
|
self.handle_ladder()
|
||||||
|
elif key == KeyValues.DANCE:
|
||||||
|
self.player.dance()
|
||||||
|
self.map.tick(self.player)
|
||||||
|
|
||||||
def handle_ladder(self) -> None:
|
def handle_ladder(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -248,6 +260,36 @@ class Game:
|
||||||
self.is_in_store_menu = True
|
self.is_in_store_menu = True
|
||||||
self.store_menu.update_merchant(entity)
|
self.store_menu.update_merchant(entity)
|
||||||
self.display_actions(DisplayActions.UPDATE)
|
self.display_actions(DisplayActions.UPDATE)
|
||||||
|
elif entity.is_chest():
|
||||||
|
self.state = GameMode.CHEST
|
||||||
|
self.is_in_chest_menu = True
|
||||||
|
self.chest_menu.update_chest(entity)
|
||||||
|
self.display_actions(DisplayActions.UPDATE)
|
||||||
|
|
||||||
|
def handle_launch(self, key: KeyValues) -> None:
|
||||||
|
"""
|
||||||
|
If the player tries to throw something in a direction, the game looks
|
||||||
|
for entities in that direction and within the range of the player's
|
||||||
|
weapon and adds damage
|
||||||
|
"""
|
||||||
|
if not self.waiting_for_launch_key:
|
||||||
|
return
|
||||||
|
self.waiting_for_launch_key = False
|
||||||
|
|
||||||
|
if key == KeyValues.UP:
|
||||||
|
direction = 0
|
||||||
|
elif key == KeyValues.DOWN:
|
||||||
|
direction = 2
|
||||||
|
elif key == KeyValues.LEFT:
|
||||||
|
direction = 3
|
||||||
|
elif key == KeyValues.RIGHT:
|
||||||
|
direction = 1
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.player.equipped_main:
|
||||||
|
if self.player.equipped_main.throw(direction):
|
||||||
|
self.map.tick(self.player)
|
||||||
|
|
||||||
def handle_key_pressed_inventory(self, key: KeyValues) -> None:
|
def handle_key_pressed_inventory(self, key: KeyValues) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -304,6 +346,36 @@ class Game:
|
||||||
# Ensure that the cursor has a good position
|
# Ensure that the cursor has a good position
|
||||||
menu.position = min(menu.position, len(menu.values) - 1)
|
menu.position = min(menu.position, len(menu.values) - 1)
|
||||||
|
|
||||||
|
def handle_key_pressed_chest(self, key: KeyValues) -> None:
|
||||||
|
"""
|
||||||
|
In a chest menu, we can take or put items or close the menu.
|
||||||
|
"""
|
||||||
|
menu = self.chest_menu if self.is_in_chest_menu else self.inventory_menu
|
||||||
|
|
||||||
|
if key == KeyValues.SPACE or key == KeyValues.INVENTORY:
|
||||||
|
self.state = GameMode.PLAY
|
||||||
|
elif key == KeyValues.UP:
|
||||||
|
menu.go_up()
|
||||||
|
elif key == KeyValues.DOWN:
|
||||||
|
menu.go_down()
|
||||||
|
elif key == KeyValues.LEFT:
|
||||||
|
self.is_in_chest_menu = False
|
||||||
|
self.display_actions(DisplayActions.UPDATE)
|
||||||
|
elif key == KeyValues.RIGHT:
|
||||||
|
self.is_in_chest_menu = True
|
||||||
|
self.display_actions(DisplayActions.UPDATE)
|
||||||
|
if menu.values and not self.player.dead:
|
||||||
|
if key == KeyValues.ENTER:
|
||||||
|
item = menu.validate()
|
||||||
|
owner = self.chest_menu.chest if self.is_in_chest_menu \
|
||||||
|
else self.player
|
||||||
|
buyer = self.player if self.is_in_chest_menu \
|
||||||
|
else self.chest_menu.chest
|
||||||
|
item.be_sold(buyer, owner, for_free=True)
|
||||||
|
self.display_actions(DisplayActions.UPDATE)
|
||||||
|
# Ensure that the cursor has a good position
|
||||||
|
menu.position = min(menu.position, len(menu.values) - 1)
|
||||||
|
|
||||||
def handle_key_pressed_main_menu(self, key: KeyValues) -> None:
|
def handle_key_pressed_main_menu(self, key: KeyValues) -> None:
|
||||||
"""
|
"""
|
||||||
In the main menu, we can navigate through different options.
|
In the main menu, we can navigate through different options.
|
||||||
|
@ -345,9 +417,10 @@ class Game:
|
||||||
self.maps = [Map().load_state(map_dict) for map_dict in d["maps"]]
|
self.maps = [Map().load_state(map_dict) for map_dict in d["maps"]]
|
||||||
for i, m in enumerate(self.maps):
|
for i, m in enumerate(self.maps):
|
||||||
m.floor = i
|
m.floor = i
|
||||||
except KeyError:
|
except KeyError as error:
|
||||||
self.message = _("Some keys are missing in your save file.\n"
|
self.message = _("Some keys are missing in your save file.\n"
|
||||||
"Your save seems to be corrupt. It got deleted.")
|
"Your save seems to be corrupt. It got deleted.")\
|
||||||
|
+ f"\n{error}"
|
||||||
os.unlink(ResourceManager.get_config_path("save.json"))
|
os.unlink(ResourceManager.get_config_path("save.json"))
|
||||||
self.display_actions(DisplayActions.UPDATE)
|
self.display_actions(DisplayActions.UPDATE)
|
||||||
return
|
return
|
||||||
|
@ -361,6 +434,7 @@ class Game:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.player = players[0]
|
self.player = players[0]
|
||||||
|
self.inventory_menu.update_player(self.player)
|
||||||
self.map.compute_visibility(self.player.y, self.player.x,
|
self.map.compute_visibility(self.player.y, self.player.x,
|
||||||
self.player.vision)
|
self.player.vision)
|
||||||
self.display_actions(DisplayActions.UPDATE)
|
self.display_actions(DisplayActions.UPDATE)
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from enum import Enum, auto
|
from copy import deepcopy
|
||||||
from math import ceil, sqrt
|
from enum import auto, Enum
|
||||||
from itertools import product
|
|
||||||
from random import choice, choices, randint
|
|
||||||
from typing import List, Optional, Any, Dict, Tuple
|
|
||||||
from queue import PriorityQueue
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from itertools import product
|
||||||
|
from math import ceil, sqrt
|
||||||
|
from queue import PriorityQueue
|
||||||
|
from random import choice, randint
|
||||||
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from .display.texturepack import TexturePack
|
from .display.texturepack import TexturePack
|
||||||
from .translations import gettext as _
|
from .translations import gettext as _
|
||||||
|
@ -180,21 +181,15 @@ class Map:
|
||||||
return "\n".join("".join(tile.char(pack) for tile in line)
|
return "\n".join("".join(tile.char(pack) for tile in line)
|
||||||
for line in self.tiles)
|
for line in self.tiles)
|
||||||
|
|
||||||
def spawn_random_entities(self, count: int) -> None:
|
def is_visible_from(self, starty: int, startx: int, desty: int, destx: int,
|
||||||
"""
|
max_range: int) -> bool:
|
||||||
Puts randomly {count} entities on the map, only on empty ground tiles.
|
oldvisibility = deepcopy(self.visibility)
|
||||||
"""
|
oldseen = deepcopy(self.seen_tiles)
|
||||||
for _ignored in range(count):
|
self.compute_visibility(starty, startx, max_range)
|
||||||
y, x = 0, 0
|
result = self.visibility[desty][destx]
|
||||||
while True:
|
self.visibility = oldvisibility
|
||||||
y, x = randint(0, self.height - 1), randint(0, self.width - 1)
|
self.seen_tiles = oldseen
|
||||||
tile = self.tiles[y][x]
|
return result
|
||||||
if tile.can_walk():
|
|
||||||
break
|
|
||||||
entity = choices(Entity.get_all_entity_classes(),
|
|
||||||
weights=Entity.get_weights(), k=1)[0]()
|
|
||||||
entity.move(y, x)
|
|
||||||
self.add_entity(entity)
|
|
||||||
|
|
||||||
def compute_visibility(self, y: int, x: int, max_range: int) -> None:
|
def compute_visibility(self, y: int, x: int, max_range: int) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -249,9 +244,9 @@ class Map:
|
||||||
continue
|
continue
|
||||||
is_opaque = self.is_wall(y, x, octant, origin)
|
is_opaque = self.is_wall(y, x, octant, origin)
|
||||||
is_visible = is_opaque\
|
is_visible = is_opaque\
|
||||||
or ((y != top_y or top > Slope(y * 4 - 1, x * 4 + 1))
|
or ((y != top_y or top >= Slope(y, x))
|
||||||
and (y != bottom_y
|
and (y != bottom_y
|
||||||
or bottom < Slope(y * 4 + 1, x * 4 - 1)))
|
or bottom <= Slope(y, x)))
|
||||||
# is_visible = is_opaque\
|
# is_visible = is_opaque\
|
||||||
# or ((y != top_y or top >= Slope(y, x))
|
# or ((y != top_y or top >= Slope(y, x))
|
||||||
# and (y != bottom_y or bottom <= Slope(y, x)))
|
# and (y != bottom_y or bottom <= Slope(y, x)))
|
||||||
|
@ -619,6 +614,13 @@ class Entity:
|
||||||
from squirrelbattle.entities.friendly import Merchant
|
from squirrelbattle.entities.friendly import Merchant
|
||||||
return isinstance(self, Merchant)
|
return isinstance(self, Merchant)
|
||||||
|
|
||||||
|
def is_chest(self) -> bool:
|
||||||
|
"""
|
||||||
|
Is this entity a chest?
|
||||||
|
"""
|
||||||
|
from squirrelbattle.entities.friendly import Chest
|
||||||
|
return isinstance(self, Chest)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def translated_name(self) -> str:
|
def translated_name(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -635,9 +637,10 @@ class Entity:
|
||||||
from squirrelbattle.entities.monsters import Tiger, Hedgehog, \
|
from squirrelbattle.entities.monsters import Tiger, Hedgehog, \
|
||||||
Rabbit, TeddyBear, GiantSeaEagle
|
Rabbit, TeddyBear, GiantSeaEagle
|
||||||
from squirrelbattle.entities.friendly import Merchant, Sunflower, \
|
from squirrelbattle.entities.friendly import Merchant, Sunflower, \
|
||||||
Trumpet
|
Trumpet, Chest
|
||||||
return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear,
|
return [BodySnatchPotion, Bomb, Chest, GiantSeaEagle, Heart,
|
||||||
Sunflower, Tiger, Merchant, GiantSeaEagle, Trumpet]
|
Hedgehog, Merchant, Rabbit, Sunflower, TeddyBear, Tiger,
|
||||||
|
Trumpet]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_weights() -> list:
|
def get_weights() -> list:
|
||||||
|
@ -645,8 +648,7 @@ class Entity:
|
||||||
Returns a weigth list associated to the above function, to
|
Returns a weigth list associated to the above function, to
|
||||||
be used to spawn random entities with a certain probability.
|
be used to spawn random entities with a certain probability.
|
||||||
"""
|
"""
|
||||||
return [3, 5, 6, 5, 5, 5,
|
return [30, 80, 50, 1, 100, 100, 60, 70, 70, 20, 40, 40]
|
||||||
5, 4, 4, 1, 2]
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_entity_classes_in_a_dict() -> dict:
|
def get_all_entity_classes_in_a_dict() -> dict:
|
||||||
|
@ -657,30 +659,37 @@ class Entity:
|
||||||
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \
|
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \
|
||||||
TeddyBear, GiantSeaEagle
|
TeddyBear, GiantSeaEagle
|
||||||
from squirrelbattle.entities.friendly import Merchant, Sunflower, \
|
from squirrelbattle.entities.friendly import Merchant, Sunflower, \
|
||||||
Trumpet
|
Trumpet, Chest
|
||||||
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \
|
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \
|
||||||
Heart, Monocle, Sword, Shield, Chestplate, Helmet, \
|
Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \
|
||||||
RingCritical, RingXP
|
ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff, \
|
||||||
|
Monocle
|
||||||
return {
|
return {
|
||||||
"Bomb": Bomb,
|
|
||||||
"Chestplate": Chestplate,
|
|
||||||
"Heart": Heart,
|
|
||||||
"BodySnatchPotion": BodySnatchPotion,
|
"BodySnatchPotion": BodySnatchPotion,
|
||||||
"Eagle": GiantSeaEagle,
|
"Bomb": Bomb,
|
||||||
|
"Bow": Bow,
|
||||||
|
"Chest": Chest,
|
||||||
|
"Chestplate": Chestplate,
|
||||||
|
"FireBallStaff": FireBallStaff,
|
||||||
|
"GiantSeaEagle": GiantSeaEagle,
|
||||||
|
"Heart": Heart,
|
||||||
"Hedgehog": Hedgehog,
|
"Hedgehog": Hedgehog,
|
||||||
"Helmet": Helmet,
|
"Helmet": Helmet,
|
||||||
"Player": Player,
|
|
||||||
"Merchant": Merchant,
|
"Merchant": Merchant,
|
||||||
"Monocle": Monocle,
|
"Monocle": Monocle,
|
||||||
"Sunflower": Sunflower,
|
"Player": Player,
|
||||||
"Sword": Sword,
|
|
||||||
"Trumpet": Trumpet,
|
|
||||||
"Shield": Shield,
|
|
||||||
"TeddyBear": TeddyBear,
|
|
||||||
"Tiger": Tiger,
|
|
||||||
"Rabbit": Rabbit,
|
"Rabbit": Rabbit,
|
||||||
"RingCritical": RingCritical,
|
"RingCritical": RingCritical,
|
||||||
"RingXP": RingXP,
|
"RingXP": RingXP,
|
||||||
|
"Ruler": Ruler,
|
||||||
|
"ScrollofDamage": ScrollofDamage,
|
||||||
|
"ScrollofWeakening": ScrollofWeakening,
|
||||||
|
"Shield": Shield,
|
||||||
|
"Sunflower": Sunflower,
|
||||||
|
"Sword": Sword,
|
||||||
|
"Trumpet": Trumpet,
|
||||||
|
"TeddyBear": TeddyBear,
|
||||||
|
"Tiger": Tiger,
|
||||||
}
|
}
|
||||||
|
|
||||||
def save_state(self) -> dict:
|
def save_state(self) -> dict:
|
||||||
|
@ -708,6 +717,7 @@ class FightingEntity(Entity):
|
||||||
constitution: int
|
constitution: int
|
||||||
level: int
|
level: int
|
||||||
critical: int
|
critical: int
|
||||||
|
confused: int # Seulement 0 ou 1
|
||||||
|
|
||||||
def __init__(self, maxhealth: int = 0, health: Optional[int] = None,
|
def __init__(self, maxhealth: int = 0, health: Optional[int] = None,
|
||||||
strength: int = 0, intelligence: int = 0, charisma: int = 0,
|
strength: int = 0, intelligence: int = 0, charisma: int = 0,
|
||||||
|
@ -723,6 +733,8 @@ class FightingEntity(Entity):
|
||||||
self.constitution = constitution
|
self.constitution = constitution
|
||||||
self.level = level
|
self.level = level
|
||||||
self.critical = critical
|
self.critical = critical
|
||||||
|
self.effects = [] # effects = temporary buff or weakening of the stats.
|
||||||
|
self.confused = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dead(self) -> bool:
|
def dead(self) -> bool:
|
||||||
|
@ -731,13 +743,31 @@ class FightingEntity(Entity):
|
||||||
"""
|
"""
|
||||||
return self.health <= 0
|
return self.health <= 0
|
||||||
|
|
||||||
|
def act(self, m: Map) -> None:
|
||||||
|
"""
|
||||||
|
Refreshes all current effects.
|
||||||
|
"""
|
||||||
|
for i in range(len(self.effects)):
|
||||||
|
self.effects[i][2] -= 1
|
||||||
|
|
||||||
|
copy = self.effects[:]
|
||||||
|
for i in range(len(copy)):
|
||||||
|
if copy[i][2] <= 0:
|
||||||
|
setattr(self, copy[i][0],
|
||||||
|
getattr(self, copy[i][0]) - copy[i][1])
|
||||||
|
self.effects.remove(copy[i])
|
||||||
|
|
||||||
def hit(self, opponent: "FightingEntity") -> str:
|
def hit(self, opponent: "FightingEntity") -> str:
|
||||||
"""
|
"""
|
||||||
The entity deals damage to the opponent
|
The entity deals damage to the opponent
|
||||||
based on their respective stats.
|
based on their respective stats.
|
||||||
"""
|
"""
|
||||||
|
if self.confused:
|
||||||
|
return _("{name} is confused, it can not hit {opponent}.")\
|
||||||
|
.format(name=_(self.translated_name.capitalize()),
|
||||||
|
opponent=_(opponent.translated_name))
|
||||||
diceroll = randint(1, 100)
|
diceroll = randint(1, 100)
|
||||||
damage = self.strength
|
damage = max(0, self.strength)
|
||||||
string = " "
|
string = " "
|
||||||
if diceroll <= self.critical: # It is a critical hit
|
if diceroll <= self.critical: # It is a critical hit
|
||||||
damage *= 4
|
damage *= 4
|
||||||
|
@ -752,7 +782,9 @@ class FightingEntity(Entity):
|
||||||
The entity takes damage from the attacker
|
The entity takes damage from the attacker
|
||||||
based on their respective stats.
|
based on their respective stats.
|
||||||
"""
|
"""
|
||||||
damage = max(0, amount - self.constitution)
|
damage = 0
|
||||||
|
if amount != 0:
|
||||||
|
damage = max(1, amount - self.constitution)
|
||||||
self.health -= damage
|
self.health -= damage
|
||||||
if self.health <= 0:
|
if self.health <= 0:
|
||||||
self.die()
|
self.die()
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
||||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
||||||
"POT-Creation-Date: 2021-01-08 15:15+0100\n"
|
"POT-Creation-Date: 2021-01-10 21:30+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -17,116 +17,158 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "ring_of_critical_damage"
|
#: squirrelbattle/display/gamedisplay.py:150
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "ring_of_more_experience"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "{name} takes {amount} damage."
|
|
||||||
msgstr "{name} nimmt {amount} Schadenspunkte."
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:28
|
|
||||||
#: squirrelbattle/display/menudisplay.py:123
|
|
||||||
#: squirrelbattle/display/menudisplay.py:148
|
|
||||||
msgid "Credits"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:32
|
|
||||||
msgid "Developers:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:38
|
|
||||||
msgid "Translators:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:168
|
|
||||||
msgid "INVENTORY"
|
|
||||||
msgstr "BESTAND"
|
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:214
|
|
||||||
msgid "STALL"
|
|
||||||
msgstr "STAND"
|
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:44
|
|
||||||
msgid "Inventory:"
|
msgid "Inventory:"
|
||||||
msgstr "Bestand:"
|
msgstr "Bestand:"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:61
|
#: squirrelbattle/display/gamedisplay.py:167
|
||||||
msgid "Equipped main:"
|
msgid "Equipped main:"
|
||||||
msgstr ""
|
msgstr "Hauptausgestattete Ding"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:65
|
#: squirrelbattle/display/gamedisplay.py:171
|
||||||
msgid "Equipped secondary:"
|
msgid "Equipped secondary:"
|
||||||
msgstr ""
|
msgstr "zusätzlich Ausgestattete Ding"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:70
|
#: squirrelbattle/display/gamedisplay.py:176
|
||||||
msgid "Equipped chestplate:"
|
msgid "Equipped chestplate:"
|
||||||
msgstr ""
|
msgstr "Ausgestattet Harnisch"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:74
|
#: squirrelbattle/display/gamedisplay.py:180
|
||||||
msgid "Equipped helmet:"
|
msgid "Equipped helmet:"
|
||||||
msgstr ""
|
msgstr "Ausgestattet Helm"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:81
|
#: squirrelbattle/display/gamedisplay.py:187
|
||||||
msgid "YOU ARE DEAD"
|
msgid "YOU ARE DEAD"
|
||||||
msgstr "SIE WURDEN GESTORBEN"
|
msgstr "SIE WURDEN GESTORBEN"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:85
|
#: squirrelbattle/display/gamedisplay.py:191
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Use {key} to use the ladder"
|
msgid "Use {key} to use the ladder"
|
||||||
msgstr ""
|
msgstr "Nutzen {key} um die Leiter zu nutzen"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:94
|
#: squirrelbattle/display/gamedisplay.py:210
|
||||||
msgid "Move to the friendly entity to talk to it"
|
msgid "Move to the friendly entity to talk to it"
|
||||||
msgstr ""
|
msgstr "Ziehen Sie zu der freundlichen Einheit hin, um mit ihr zu sprechen"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:96
|
#: squirrelbattle/display/gamedisplay.py:212
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Use {key} then move to talk to the entity"
|
msgid "Use {key} then move to talk to the entity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Verwenden Sie {key} dann bewegen Sie sich, um mit der Einheit zu sprechen"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:124
|
||||||
|
#: squirrelbattle/display/menudisplay.py:149
|
||||||
|
#: squirrelbattle/display/menudisplay.py:304
|
||||||
|
msgid "Credits"
|
||||||
|
msgstr "Abspann"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:173
|
||||||
|
msgid "INVENTORY"
|
||||||
|
msgstr "BESTAND"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:219
|
||||||
|
msgid "STALL"
|
||||||
|
msgstr "STAND"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:263
|
||||||
|
msgid "CHEST"
|
||||||
|
msgstr "KASTE"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:308
|
||||||
|
msgid "Developers:"
|
||||||
|
msgstr "Entwickler:"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:314
|
||||||
|
msgid "Translators:"
|
||||||
|
msgstr "Ubersetzer:"
|
||||||
|
|
||||||
#. TODO
|
#. TODO
|
||||||
#: squirrelbattle/entities/friendly.py:33
|
#: squirrelbattle/entities/friendly.py:38
|
||||||
msgid "I don't sell any squirrel"
|
msgid "I don't sell any squirrel"
|
||||||
msgstr "Ich verkaufe keinen Eichhörnchen."
|
msgstr "Ich verkaufe keinen Eichhörnchen."
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:55
|
#: squirrelbattle/entities/friendly.py:68
|
||||||
|
msgid "You have opened the chest"
|
||||||
|
msgstr "Sie haben der Kaste geöffnet"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:77
|
||||||
|
msgid "The chest exploded"
|
||||||
|
msgstr "Der Kaste explodierte"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:78
|
||||||
|
msgid "It's not really effective"
|
||||||
|
msgstr "Es ist nicht wirklich effektiv"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:101
|
||||||
msgid "Flower power!!"
|
msgid "Flower power!!"
|
||||||
msgstr "Blumenmacht!!"
|
msgstr "Blumenmacht!!"
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:55
|
#: squirrelbattle/entities/friendly.py:101
|
||||||
msgid "The sun is warm today"
|
msgid "The sun is warm today"
|
||||||
msgstr "Die Sonne ist warm heute"
|
msgstr "Die Sonne ist warm heute"
|
||||||
|
|
||||||
#. The bomb is exploding.
|
#. The bomb is exploding.
|
||||||
#. Each entity that is close to the bomb takes damages.
|
#. Each entity that is close to the bomb takes damages.
|
||||||
#. The player earn XP if the entity was killed.
|
#. The player earn XP if the entity was killed.
|
||||||
#: squirrelbattle/entities/items.py:178
|
#: squirrelbattle/entities/items.py:189
|
||||||
msgid "Bomb is exploding."
|
msgid "Bomb is exploding."
|
||||||
msgstr "Die Bombe explodiert."
|
msgstr "Die Bombe explodiert."
|
||||||
|
|
||||||
#: squirrelbattle/entities/items.py:365
|
#: squirrelbattle/entities/items.py:385
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{player} exchanged its body with {entity}."
|
msgid "{player} exchanged its body with {entity}."
|
||||||
msgstr "{player} täuscht seinem Körper mit {entity} aus."
|
msgstr "{player} täuscht seinem Körper mit {entity} aus."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:200
|
#: squirrelbattle/entities/items.py:519
|
||||||
|
msgid ""
|
||||||
|
"The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 "
|
||||||
|
"turns"
|
||||||
|
msgstr ""
|
||||||
|
"Die Feinde haben 3 Runden lang - {max(1, self.held_by.intelligence // 2)} "
|
||||||
|
"Stärke"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:552
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name}"
|
||||||
|
msgstr "{name}"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:600
|
||||||
|
msgid " is shot by an arrow."
|
||||||
|
msgstr " wird von einem Pfeil erschossen."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:622
|
||||||
|
msgid " is shot by a fire ball."
|
||||||
|
msgstr " wird von eine Feuerball erschossen."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:83
|
||||||
|
msgid "It worked! Nearby ennemies will be confused for 3 turns."
|
||||||
|
msgstr ""
|
||||||
|
"Es funktionierte! In der Nähe befindliche Feinde werden 3 Runden lang "
|
||||||
|
"verwirrt."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:86
|
||||||
|
msgid "It worked, but there is no one nearby..."
|
||||||
|
msgstr "Es hat funktioniert, aber es ist niemand in der Nähe ..."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:89
|
||||||
|
msgid "The dance was not effective..."
|
||||||
|
msgstr "Der Tanz war nicht effektiv ..."
|
||||||
|
|
||||||
|
#: squirrelbattle/game.py:214
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The player climbs down to the floor {floor}."
|
msgid "The player climbs down to the floor {floor}."
|
||||||
msgstr "Der Spieler klettert auf dem Stock {floor} hinunter."
|
msgstr "Der Spieler klettert auf dem Stock {floor} hinunter."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:213
|
#: squirrelbattle/game.py:227
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The player climbs up the floor {floor}."
|
msgid "The player climbs up the floor {floor}."
|
||||||
msgstr "Der Spieler klettert auf dem Stock {floor} hinoben."
|
msgstr "Der Spieler klettert auf dem Stock {floor} hinoben."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603
|
#: squirrelbattle/game.py:348 squirrelbattle/tests/game_test.py:631
|
||||||
msgid "The buyer does not have enough money"
|
msgid "The buyer does not have enough money"
|
||||||
msgstr "Der Kaufer hat nicht genug Geld"
|
msgstr "Der Kaufer hat nicht genug Geld"
|
||||||
|
|
||||||
#: squirrelbattle/game.py:349
|
#: squirrelbattle/game.py:423
|
||||||
msgid ""
|
msgid ""
|
||||||
"Some keys are missing in your save file.\n"
|
"Some keys are missing in your save file.\n"
|
||||||
"Your save seems to be corrupt. It got deleted."
|
"Your save seems to be corrupt. It got deleted."
|
||||||
|
@ -134,7 +176,7 @@ msgstr ""
|
||||||
"In Ihrer Speicherdatei fehlen einige Schlüssel.\n"
|
"In Ihrer Speicherdatei fehlen einige Schlüssel.\n"
|
||||||
"Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht."
|
"Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:357
|
#: squirrelbattle/game.py:431
|
||||||
msgid ""
|
msgid ""
|
||||||
"No player was found on this map!\n"
|
"No player was found on this map!\n"
|
||||||
"Maybe you died?"
|
"Maybe you died?"
|
||||||
|
@ -142,7 +184,7 @@ msgstr ""
|
||||||
"Auf dieser Karte wurde kein Spieler gefunden!\n"
|
"Auf dieser Karte wurde kein Spieler gefunden!\n"
|
||||||
"Vielleicht sind Sie gestorben?"
|
"Vielleicht sind Sie gestorben?"
|
||||||
|
|
||||||
#: squirrelbattle/game.py:379
|
#: squirrelbattle/game.py:454
|
||||||
msgid ""
|
msgid ""
|
||||||
"The JSON file is not correct.\n"
|
"The JSON file is not correct.\n"
|
||||||
"Your save seems corrupted. It got deleted."
|
"Your save seems corrupted. It got deleted."
|
||||||
|
@ -150,26 +192,31 @@ msgstr ""
|
||||||
"Die JSON-Datei ist nicht korrekt.\n"
|
"Die JSON-Datei ist nicht korrekt.\n"
|
||||||
"Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht."
|
"Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:718
|
#: squirrelbattle/interfaces.py:758 squirrelbattle/tests/game_test.py:264
|
||||||
msgid "It's a critical hit!"
|
#, python-brace-format
|
||||||
msgstr ""
|
msgid "{name} is confused, it can not hit {opponent}."
|
||||||
|
msgstr "{name} ist verwirrt, es kann {opponent} nicht schlagen."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:719
|
#: squirrelbattle/interfaces.py:766
|
||||||
|
msgid "It's a critical hit!"
|
||||||
|
msgstr "Es ist ein kritischer Treffer!"
|
||||||
|
|
||||||
|
#: squirrelbattle/interfaces.py:767
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} hits {opponent}."
|
msgid "{name} hits {opponent}."
|
||||||
msgstr "{name} schlägt {opponent}."
|
msgstr "{name} schlägt {opponent}."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:733
|
#: squirrelbattle/interfaces.py:782
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} takes {damage} damage."
|
msgid "{name} takes {damage} damage."
|
||||||
msgstr ""
|
msgstr "{name} erleidet {damage} Schaden."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:735
|
#: squirrelbattle/interfaces.py:784
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} dies."
|
msgid "{name} dies."
|
||||||
msgstr "{name} stirbt."
|
msgstr "{name} stirbt."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:769
|
#: squirrelbattle/interfaces.py:818
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{entity} said: {message}"
|
msgid "{entity} said: {message}"
|
||||||
msgstr "{entity} hat gesagt: {message}"
|
msgstr "{entity} hat gesagt: {message}"
|
||||||
|
@ -178,8 +225,8 @@ msgstr "{entity} hat gesagt: {message}"
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr "Zurück"
|
msgstr "Zurück"
|
||||||
|
|
||||||
#: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371
|
#: squirrelbattle/tests/game_test.py:395 squirrelbattle/tests/game_test.py:398
|
||||||
#: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377
|
#: squirrelbattle/tests/game_test.py:401 squirrelbattle/tests/game_test.py:404
|
||||||
#: squirrelbattle/tests/translations_test.py:16
|
#: squirrelbattle/tests/translations_test.py:16
|
||||||
msgid "New game"
|
msgid "New game"
|
||||||
msgstr "Neu Spiel"
|
msgstr "Neu Spiel"
|
||||||
|
@ -269,85 +316,113 @@ msgid "Key used to use ladders"
|
||||||
msgstr "Leitertaste"
|
msgstr "Leitertaste"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:58
|
#: squirrelbattle/tests/translations_test.py:58
|
||||||
|
msgid "Key used to use a bow"
|
||||||
|
msgstr "Bogentaste"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:60
|
||||||
|
msgid "Key used to dance"
|
||||||
|
msgstr "Tanztaste"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:62
|
||||||
msgid "Texture pack"
|
msgid "Texture pack"
|
||||||
msgstr "Textur-Packung"
|
msgstr "Textur-Packung"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:59
|
#: squirrelbattle/tests/translations_test.py:63
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Sprache"
|
msgstr "Sprache"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:62
|
#: squirrelbattle/tests/translations_test.py:66
|
||||||
msgid "player"
|
msgid "player"
|
||||||
msgstr "Spieler"
|
msgstr "Spieler"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:64
|
#: squirrelbattle/tests/translations_test.py:68
|
||||||
msgid "hedgehog"
|
msgid "hedgehog"
|
||||||
msgstr "Igel"
|
msgstr "Igel"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:65
|
#: squirrelbattle/tests/translations_test.py:69
|
||||||
msgid "merchant"
|
msgid "merchant"
|
||||||
msgstr "Kaufmann"
|
msgstr "Kaufmann"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:66
|
#: squirrelbattle/tests/translations_test.py:70
|
||||||
msgid "rabbit"
|
msgid "rabbit"
|
||||||
msgstr "Kanninchen"
|
msgstr "Kanninchen"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:67
|
#: squirrelbattle/tests/translations_test.py:71
|
||||||
msgid "sunflower"
|
msgid "sunflower"
|
||||||
msgstr "Sonnenblume"
|
msgstr "Sonnenblume"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:68
|
#: squirrelbattle/tests/translations_test.py:72
|
||||||
msgid "teddy bear"
|
msgid "teddy bear"
|
||||||
msgstr "Teddybär"
|
msgstr "Teddybär"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:69
|
#: squirrelbattle/tests/translations_test.py:73
|
||||||
msgid "tiger"
|
msgid "tiger"
|
||||||
msgstr "Tiger"
|
msgstr "Tiger"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:70
|
#: squirrelbattle/tests/translations_test.py:74
|
||||||
msgid "eagle"
|
msgid "eagle"
|
||||||
msgstr ""
|
msgstr "Adler"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:72
|
#: squirrelbattle/tests/translations_test.py:76
|
||||||
msgid "body snatch potion"
|
msgid "body snatch potion"
|
||||||
msgstr "Leichenfleddererzaubertrank"
|
msgstr "Leichenfleddererzaubertrank"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:73
|
#: squirrelbattle/tests/translations_test.py:77
|
||||||
msgid "bomb"
|
msgid "bomb"
|
||||||
msgstr "Bombe"
|
msgstr "Bombe"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:74
|
#: squirrelbattle/tests/translations_test.py:78
|
||||||
msgid "explosion"
|
msgid "explosion"
|
||||||
msgstr "Explosion"
|
msgstr "Explosion"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:75
|
#: squirrelbattle/tests/translations_test.py:79
|
||||||
msgid "heart"
|
msgid "heart"
|
||||||
msgstr "Herz"
|
msgstr "Herz"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:76
|
#: squirrelbattle/tests/translations_test.py:80
|
||||||
msgid "sword"
|
msgid "sword"
|
||||||
msgstr "schwert"
|
msgstr "schwert"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:77
|
#: squirrelbattle/tests/translations_test.py:81
|
||||||
msgid "helmet"
|
msgid "helmet"
|
||||||
msgstr ""
|
msgstr "Helm"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:78
|
|
||||||
msgid "chestplate"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:79
|
|
||||||
msgid "shield"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:80
|
|
||||||
msgid "ring of critical damage"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:82
|
#: squirrelbattle/tests/translations_test.py:82
|
||||||
msgid "ring of more experience"
|
msgid "chestplate"
|
||||||
msgstr ""
|
msgstr "Brustpanzer"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:83
|
||||||
|
msgid "shield"
|
||||||
|
msgstr "Schild"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:84
|
#: squirrelbattle/tests/translations_test.py:84
|
||||||
|
msgid "ruler"
|
||||||
|
msgstr "Lineal"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:85
|
||||||
|
msgid "scroll of damage"
|
||||||
|
msgstr "Schriftrolle des Schadens"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:86
|
||||||
|
msgid "scroll of weakness"
|
||||||
|
msgstr "Schriftrolle der Schwäche"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:87
|
||||||
|
msgid "bow"
|
||||||
|
msgstr "Bogen"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:88
|
||||||
|
msgid "fire ball staff"
|
||||||
|
msgstr "Feuerball Stab"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:89
|
||||||
|
msgid "ring of critical damage"
|
||||||
|
msgstr "Ring des kritischen Schadens"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:91
|
||||||
|
msgid "ring of more experience"
|
||||||
|
msgstr "Ring der mehr Erfahrung"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:93
|
||||||
msgid "monocle"
|
msgid "monocle"
|
||||||
msgstr ""
|
msgstr "Monokel"
|
||||||
|
|
|
@ -1,207 +0,0 @@
|
||||||
# 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-12-01 17:10+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
|
|
||||||
#: squirrelbattle/interfaces.py:408
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "{name} hits {opponent}."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:405 squirrelbattle/interfaces.py:410
|
|
||||||
#: squirrelbattle/interfaces.py:420
|
|
||||||
#, 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
|
|
||||||
#: squirrelbattle/tests/game_test.py:290
|
|
||||||
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
|
|
||||||
#: squirrelbattle/interfaces.py:422
|
|
||||||
#, 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 ""
|
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:31
|
|
||||||
msgid "Flower power!!"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:31
|
|
||||||
msgid "The sun is warm today"
|
|
||||||
msgstr ""
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
||||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
||||||
"POT-Creation-Date: 2021-01-08 15:15+0100\n"
|
"POT-Creation-Date: 2021-01-10 21:30+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -17,115 +17,154 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "ring_of_critical_damage"
|
#: squirrelbattle/display/gamedisplay.py:150
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "ring_of_more_experience"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "{name} takes {amount} damage."
|
|
||||||
msgstr "{name} recibe {amount} daño."
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:28
|
|
||||||
#: squirrelbattle/display/menudisplay.py:123
|
|
||||||
#: squirrelbattle/display/menudisplay.py:148
|
|
||||||
msgid "Credits"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:32
|
|
||||||
msgid "Developers:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:38
|
|
||||||
msgid "Translators:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:168
|
|
||||||
msgid "INVENTORY"
|
|
||||||
msgstr "INVENTORIO"
|
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:214
|
|
||||||
msgid "STALL"
|
|
||||||
msgstr "PUESTO"
|
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:44
|
|
||||||
msgid "Inventory:"
|
msgid "Inventory:"
|
||||||
msgstr "Inventorio :"
|
msgstr "Inventorio :"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:61
|
#: squirrelbattle/display/gamedisplay.py:167
|
||||||
msgid "Equipped main:"
|
msgid "Equipped main:"
|
||||||
msgstr ""
|
msgstr "Principal equipado:"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:65
|
#: squirrelbattle/display/gamedisplay.py:171
|
||||||
msgid "Equipped secondary:"
|
msgid "Equipped secondary:"
|
||||||
msgstr ""
|
msgstr "Equipado secundario:"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:70
|
#: squirrelbattle/display/gamedisplay.py:176
|
||||||
msgid "Equipped chestplate:"
|
msgid "Equipped chestplate:"
|
||||||
msgstr ""
|
msgstr "Pechera equipada:"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:74
|
#: squirrelbattle/display/gamedisplay.py:180
|
||||||
msgid "Equipped helmet:"
|
msgid "Equipped helmet:"
|
||||||
msgstr ""
|
msgstr "Casco equipado:"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:81
|
#: squirrelbattle/display/gamedisplay.py:187
|
||||||
msgid "YOU ARE DEAD"
|
msgid "YOU ARE DEAD"
|
||||||
msgstr "ERES MUERTO"
|
msgstr "ERES MUERTO"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:85
|
#: squirrelbattle/display/gamedisplay.py:191
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Use {key} to use the ladder"
|
msgid "Use {key} to use the ladder"
|
||||||
msgstr ""
|
msgstr "Usa {key} para usar la escalera"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:94
|
#: squirrelbattle/display/gamedisplay.py:210
|
||||||
msgid "Move to the friendly entity to talk to it"
|
msgid "Move to the friendly entity to talk to it"
|
||||||
msgstr ""
|
msgstr "Muévete hacia la entidad amiga para hablar con ella."
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:96
|
#: squirrelbattle/display/gamedisplay.py:212
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Use {key} then move to talk to the entity"
|
msgid "Use {key} then move to talk to the entity"
|
||||||
msgstr ""
|
msgstr "Usa {key} y luego muévete para hablar con la entidad"
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:33
|
#: squirrelbattle/display/menudisplay.py:124
|
||||||
|
#: squirrelbattle/display/menudisplay.py:149
|
||||||
|
#: squirrelbattle/display/menudisplay.py:304
|
||||||
|
msgid "Credits"
|
||||||
|
msgstr "Creditos"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:173
|
||||||
|
msgid "INVENTORY"
|
||||||
|
msgstr "INVENTORIO"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:219
|
||||||
|
msgid "STALL"
|
||||||
|
msgstr "PUESTO"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:263
|
||||||
|
msgid "CHEST"
|
||||||
|
msgstr "COFRE"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:308
|
||||||
|
msgid "Developers:"
|
||||||
|
msgstr "Desarrollador:"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:314
|
||||||
|
msgid "Translators:"
|
||||||
|
msgstr "Traductores:"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:38
|
||||||
msgid "I don't sell any squirrel"
|
msgid "I don't sell any squirrel"
|
||||||
msgstr "No vendo ninguna ardilla"
|
msgstr "No vendo ninguna ardilla"
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:55
|
#: squirrelbattle/entities/friendly.py:68
|
||||||
|
msgid "You have opened the chest"
|
||||||
|
msgstr "Abriste el cofre"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:77
|
||||||
|
msgid "The chest exploded"
|
||||||
|
msgstr "El cofre explotó"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:78
|
||||||
|
msgid "It's not really effective"
|
||||||
|
msgstr "No es realmente efectivo"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:101
|
||||||
msgid "Flower power!!"
|
msgid "Flower power!!"
|
||||||
msgstr "Poder de las flores!!"
|
msgstr "Poder de las flores!!"
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:55
|
#: squirrelbattle/entities/friendly.py:101
|
||||||
msgid "The sun is warm today"
|
msgid "The sun is warm today"
|
||||||
msgstr "El sol está caliente hoy"
|
msgstr "El sol está caliente hoy"
|
||||||
|
|
||||||
#. The bomb is exploding.
|
#. The bomb is exploding.
|
||||||
#. Each entity that is close to the bomb takes damages.
|
#. Each entity that is close to the bomb takes damages.
|
||||||
#. The player earn XP if the entity was killed.
|
#. The player earn XP if the entity was killed.
|
||||||
#: squirrelbattle/entities/items.py:178
|
#: squirrelbattle/entities/items.py:189
|
||||||
msgid "Bomb is exploding."
|
msgid "Bomb is exploding."
|
||||||
msgstr "La bomba está explotando."
|
msgstr "La bomba está explotando."
|
||||||
|
|
||||||
#: squirrelbattle/entities/items.py:365
|
#: squirrelbattle/entities/items.py:385
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{player} exchanged its body with {entity}."
|
msgid "{player} exchanged its body with {entity}."
|
||||||
msgstr "{player} intercambió su cuerpo con {entity}."
|
msgstr "{player} intercambió su cuerpo con {entity}."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:200
|
#: squirrelbattle/entities/items.py:519
|
||||||
|
msgid ""
|
||||||
|
"The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 "
|
||||||
|
"turns"
|
||||||
|
msgstr ""
|
||||||
|
"Los enemigos tienen - {max(1, self.held_by.intelligence // 2)} fuerza "
|
||||||
|
"durante 3turnos"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:552
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name}"
|
||||||
|
msgstr "{name}"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:600
|
||||||
|
msgid " is shot by an arrow."
|
||||||
|
msgstr " es disparado por una flecha."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:622
|
||||||
|
msgid " is shot by a fire ball."
|
||||||
|
msgstr " es disparado por una bola de fuego."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:83
|
||||||
|
msgid "It worked! Nearby ennemies will be confused for 3 turns."
|
||||||
|
msgstr "¡Funcionó! Los enemigos cercanos se confundirán durante 3 turnos."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:86
|
||||||
|
msgid "It worked, but there is no one nearby..."
|
||||||
|
msgstr "Funcionó, pero no hay nadie cerca ..."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:89
|
||||||
|
msgid "The dance was not effective..."
|
||||||
|
msgstr "El baile no fue efectivo ..."
|
||||||
|
|
||||||
|
#: squirrelbattle/game.py:214
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The player climbs down to the floor {floor}."
|
msgid "The player climbs down to the floor {floor}."
|
||||||
msgstr ""
|
msgstr "El jugador desciende alla planta {floor}."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:213
|
#: squirrelbattle/game.py:227
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The player climbs up the floor {floor}."
|
msgid "The player climbs up the floor {floor}."
|
||||||
msgstr ""
|
msgstr "El jugador sube por la planta {floor}."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603
|
#: squirrelbattle/game.py:348 squirrelbattle/tests/game_test.py:631
|
||||||
msgid "The buyer does not have enough money"
|
msgid "The buyer does not have enough money"
|
||||||
msgstr "El comprador no tiene suficiente dinero"
|
msgstr "El comprador no tiene suficiente dinero"
|
||||||
|
|
||||||
#: squirrelbattle/game.py:349
|
#: squirrelbattle/game.py:423
|
||||||
msgid ""
|
msgid ""
|
||||||
"Some keys are missing in your save file.\n"
|
"Some keys are missing in your save file.\n"
|
||||||
"Your save seems to be corrupt. It got deleted."
|
"Your save seems to be corrupt. It got deleted."
|
||||||
|
@ -133,7 +172,7 @@ msgstr ""
|
||||||
"Algunas claves faltan en su archivo de guarda.\n"
|
"Algunas claves faltan en su archivo de guarda.\n"
|
||||||
"Su guarda parece a ser corruptido. Fue eliminado."
|
"Su guarda parece a ser corruptido. Fue eliminado."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:357
|
#: squirrelbattle/game.py:431
|
||||||
msgid ""
|
msgid ""
|
||||||
"No player was found on this map!\n"
|
"No player was found on this map!\n"
|
||||||
"Maybe you died?"
|
"Maybe you died?"
|
||||||
|
@ -141,7 +180,7 @@ msgstr ""
|
||||||
"No jugador encontrado sobre la carta !\n"
|
"No jugador encontrado sobre la carta !\n"
|
||||||
"¿ Quizas murió ?"
|
"¿ Quizas murió ?"
|
||||||
|
|
||||||
#: squirrelbattle/game.py:379
|
#: squirrelbattle/game.py:454
|
||||||
msgid ""
|
msgid ""
|
||||||
"The JSON file is not correct.\n"
|
"The JSON file is not correct.\n"
|
||||||
"Your save seems corrupted. It got deleted."
|
"Your save seems corrupted. It got deleted."
|
||||||
|
@ -149,26 +188,31 @@ msgstr ""
|
||||||
"El JSON archivo no es correcto.\n"
|
"El JSON archivo no es correcto.\n"
|
||||||
"Su guarda parece corrupta. Fue eliminada."
|
"Su guarda parece corrupta. Fue eliminada."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:718
|
#: squirrelbattle/interfaces.py:758 squirrelbattle/tests/game_test.py:264
|
||||||
msgid "It's a critical hit!"
|
#, python-brace-format
|
||||||
msgstr ""
|
msgid "{name} is confused, it can not hit {opponent}."
|
||||||
|
msgstr "{name} está confundido, no puede golpear a {opponent}."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:719
|
#: squirrelbattle/interfaces.py:766
|
||||||
|
msgid "It's a critical hit!"
|
||||||
|
msgstr "¡Es un golpe crítico!"
|
||||||
|
|
||||||
|
#: squirrelbattle/interfaces.py:767
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} hits {opponent}."
|
msgid "{name} hits {opponent}."
|
||||||
msgstr "{name} golpea a {opponent}."
|
msgstr "{name} golpea a {opponent}."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:733
|
#: squirrelbattle/interfaces.py:782
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} takes {damage} damage."
|
msgid "{name} takes {damage} damage."
|
||||||
msgstr ""
|
msgstr "{name} recibe {damage} daño."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:735
|
#: squirrelbattle/interfaces.py:784
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} dies."
|
msgid "{name} dies."
|
||||||
msgstr "{name} se muere."
|
msgstr "{name} se muere."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:769
|
#: squirrelbattle/interfaces.py:818
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{entity} said: {message}"
|
msgid "{entity} said: {message}"
|
||||||
msgstr "{entity} dijo : {message}"
|
msgstr "{entity} dijo : {message}"
|
||||||
|
@ -177,8 +221,8 @@ msgstr "{entity} dijo : {message}"
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr "Volver"
|
msgstr "Volver"
|
||||||
|
|
||||||
#: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371
|
#: squirrelbattle/tests/game_test.py:395 squirrelbattle/tests/game_test.py:398
|
||||||
#: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377
|
#: squirrelbattle/tests/game_test.py:401 squirrelbattle/tests/game_test.py:404
|
||||||
#: squirrelbattle/tests/translations_test.py:16
|
#: squirrelbattle/tests/translations_test.py:16
|
||||||
msgid "New game"
|
msgid "New game"
|
||||||
msgstr "Nuevo partido"
|
msgstr "Nuevo partido"
|
||||||
|
@ -268,85 +312,113 @@ msgid "Key used to use ladders"
|
||||||
msgstr "Tecla para el uso de las escaleras"
|
msgstr "Tecla para el uso de las escaleras"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:58
|
#: squirrelbattle/tests/translations_test.py:58
|
||||||
|
msgid "Key used to use a bow"
|
||||||
|
msgstr "Tecla para usar un arco"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:60
|
||||||
|
msgid "Key used to dance"
|
||||||
|
msgstr "Tecla para bailar"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:62
|
||||||
msgid "Texture pack"
|
msgid "Texture pack"
|
||||||
msgstr "Paquete de texturas"
|
msgstr "Paquete de texturas"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:59
|
#: squirrelbattle/tests/translations_test.py:63
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Languaje"
|
msgstr "Languaje"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:62
|
#: squirrelbattle/tests/translations_test.py:66
|
||||||
msgid "player"
|
msgid "player"
|
||||||
msgstr "jugador"
|
msgstr "jugador"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:64
|
#: squirrelbattle/tests/translations_test.py:68
|
||||||
msgid "hedgehog"
|
msgid "hedgehog"
|
||||||
msgstr "erizo"
|
msgstr "erizo"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:65
|
#: squirrelbattle/tests/translations_test.py:69
|
||||||
msgid "merchant"
|
msgid "merchant"
|
||||||
msgstr "comerciante"
|
msgstr "comerciante"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:66
|
#: squirrelbattle/tests/translations_test.py:70
|
||||||
msgid "rabbit"
|
msgid "rabbit"
|
||||||
msgstr "conejo"
|
msgstr "conejo"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:67
|
#: squirrelbattle/tests/translations_test.py:71
|
||||||
msgid "sunflower"
|
msgid "sunflower"
|
||||||
msgstr "girasol"
|
msgstr "girasol"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:68
|
#: squirrelbattle/tests/translations_test.py:72
|
||||||
msgid "teddy bear"
|
msgid "teddy bear"
|
||||||
msgstr "osito de peluche"
|
msgstr "osito de peluche"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:69
|
#: squirrelbattle/tests/translations_test.py:73
|
||||||
msgid "tiger"
|
msgid "tiger"
|
||||||
msgstr "tigre"
|
msgstr "tigre"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:70
|
#: squirrelbattle/tests/translations_test.py:74
|
||||||
msgid "eagle"
|
msgid "eagle"
|
||||||
msgstr ""
|
msgstr "águila"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:72
|
#: squirrelbattle/tests/translations_test.py:76
|
||||||
msgid "body snatch potion"
|
msgid "body snatch potion"
|
||||||
msgstr "poción de intercambio"
|
msgstr "poción de intercambio"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:73
|
#: squirrelbattle/tests/translations_test.py:77
|
||||||
msgid "bomb"
|
msgid "bomb"
|
||||||
msgstr "bomba"
|
msgstr "bomba"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:74
|
#: squirrelbattle/tests/translations_test.py:78
|
||||||
msgid "explosion"
|
msgid "explosion"
|
||||||
msgstr "explosión"
|
msgstr "explosión"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:75
|
#: squirrelbattle/tests/translations_test.py:79
|
||||||
msgid "heart"
|
msgid "heart"
|
||||||
msgstr "corazón"
|
msgstr "corazón"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:76
|
#: squirrelbattle/tests/translations_test.py:80
|
||||||
msgid "sword"
|
msgid "sword"
|
||||||
msgstr "espada"
|
msgstr "espada"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:77
|
#: squirrelbattle/tests/translations_test.py:81
|
||||||
msgid "helmet"
|
msgid "helmet"
|
||||||
msgstr ""
|
msgstr "casco"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:78
|
|
||||||
msgid "chestplate"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:79
|
|
||||||
msgid "shield"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:80
|
|
||||||
msgid "ring of critical damage"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:82
|
#: squirrelbattle/tests/translations_test.py:82
|
||||||
msgid "ring of more experience"
|
msgid "chestplate"
|
||||||
msgstr ""
|
msgstr "pechera"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:83
|
||||||
|
msgid "shield"
|
||||||
|
msgstr "escudo"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:84
|
#: squirrelbattle/tests/translations_test.py:84
|
||||||
|
msgid "ruler"
|
||||||
|
msgstr "Regla"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:85
|
||||||
|
msgid "scroll of damage"
|
||||||
|
msgstr "rollo de daño"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:86
|
||||||
|
msgid "scroll of weakness"
|
||||||
|
msgstr "rollo de debilidad"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:87
|
||||||
|
msgid "bow"
|
||||||
|
msgstr "arco"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:88
|
||||||
|
msgid "fire ball staff"
|
||||||
|
msgstr "bastón de bola de fuego"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:89
|
||||||
|
msgid "ring of critical damage"
|
||||||
|
msgstr "anillo de daño crítico"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:91
|
||||||
|
msgid "ring of more experience"
|
||||||
|
msgstr "anillo de más experiencia"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:93
|
||||||
msgid "monocle"
|
msgid "monocle"
|
||||||
msgstr ""
|
msgstr "monóculo"
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} takes {amount} damage."
|
||||||
|
msgstr "{name} prend {amount} points de dégât."
|
||||||
|
|
||||||
# SOME DESCRIPTIVE TITLE.
|
# SOME DESCRIPTIVE TITLE.
|
||||||
# Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao
|
# Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao
|
||||||
# This file is distributed under the same license as the squirrelbattle package.
|
# This file is distributed under the same license as the squirrelbattle package.
|
||||||
|
@ -8,7 +12,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
||||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
||||||
"POT-Creation-Date: 2021-01-08 15:15+0100\n"
|
"POT-Creation-Date: 2021-01-10 21:30+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -17,110 +21,155 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
#, python-brace-format
|
#: squirrelbattle/display/gamedisplay.py:150
|
||||||
msgid "{name} takes {amount} damage."
|
|
||||||
msgstr "{name} prend {amount} points de dégât."
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:28
|
|
||||||
#: squirrelbattle/display/menudisplay.py:123
|
|
||||||
#: squirrelbattle/display/menudisplay.py:148
|
|
||||||
msgid "Credits"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:32
|
|
||||||
msgid "Developers:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/creditsdisplay.py:38
|
|
||||||
msgid "Translators:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:168
|
|
||||||
msgid "INVENTORY"
|
|
||||||
msgstr "INVENTAIRE"
|
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:214
|
|
||||||
msgid "STALL"
|
|
||||||
msgstr "STAND"
|
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:44
|
|
||||||
msgid "Inventory:"
|
msgid "Inventory:"
|
||||||
msgstr "Inventaire :"
|
msgstr "Inventaire :"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:61
|
#: squirrelbattle/display/gamedisplay.py:167
|
||||||
msgid "Equipped main:"
|
msgid "Equipped main:"
|
||||||
msgstr "Équipement principal :"
|
msgstr "Équipement principal :"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:65
|
#: squirrelbattle/display/gamedisplay.py:171
|
||||||
msgid "Equipped secondary:"
|
msgid "Equipped secondary:"
|
||||||
msgstr "Équipement secondaire :"
|
msgstr "Équipement secondaire :"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:70
|
#: squirrelbattle/display/gamedisplay.py:176
|
||||||
msgid "Equipped chestplate:"
|
msgid "Equipped chestplate:"
|
||||||
msgstr "Plastron équipé :"
|
msgstr "Plastron équipé :"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:74
|
#: squirrelbattle/display/gamedisplay.py:180
|
||||||
msgid "Equipped helmet:"
|
msgid "Equipped helmet:"
|
||||||
msgstr "Casque équipé :"
|
msgstr "Casque équipé :"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:81
|
#: squirrelbattle/display/gamedisplay.py:187
|
||||||
msgid "YOU ARE DEAD"
|
msgid "YOU ARE DEAD"
|
||||||
msgstr "VOUS ÊTES MORT"
|
msgstr "VOUS ÊTES MORT"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:85
|
#: squirrelbattle/display/gamedisplay.py:191
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Use {key} to use the ladder"
|
msgid "Use {key} to use the ladder"
|
||||||
msgstr "Appuyez sur {key} pour utiliser l'échelle"
|
msgstr "Appuyez sur {key} pour utiliser l'échelle"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:94
|
#: squirrelbattle/display/gamedisplay.py:210
|
||||||
msgid "Move to the friendly entity to talk to it"
|
msgid "Move to the friendly entity to talk to it"
|
||||||
msgstr "Avancez vers l'entité pour lui parler"
|
msgstr "Avancez vers l'entité pour lui parler"
|
||||||
|
|
||||||
#: squirrelbattle/display/statsdisplay.py:96
|
#: squirrelbattle/display/gamedisplay.py:212
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Use {key} then move to talk to the entity"
|
msgid "Use {key} then move to talk to the entity"
|
||||||
msgstr "Appuyez sur {key} puis déplacez-vous pour parler"
|
msgstr "Appuyez sur {key} puis déplacez-vous pour parler"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:124
|
||||||
|
#: squirrelbattle/display/menudisplay.py:149
|
||||||
|
#: squirrelbattle/display/menudisplay.py:304
|
||||||
|
msgid "Credits"
|
||||||
|
msgstr "Crédits"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:173
|
||||||
|
msgid "INVENTORY"
|
||||||
|
msgstr "INVENTAIRE"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:219
|
||||||
|
msgid "STALL"
|
||||||
|
msgstr "STAND"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:263
|
||||||
|
msgid "CHEST"
|
||||||
|
msgstr "COFFRE"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:308
|
||||||
|
msgid "Developers:"
|
||||||
|
msgstr "Développeurs:"
|
||||||
|
|
||||||
|
#: squirrelbattle/display/menudisplay.py:314
|
||||||
|
msgid "Translators:"
|
||||||
|
msgstr "Traducteurs:"
|
||||||
|
|
||||||
#. TODO
|
#. TODO
|
||||||
#: squirrelbattle/entities/friendly.py:33
|
#: squirrelbattle/entities/friendly.py:38
|
||||||
msgid "I don't sell any squirrel"
|
msgid "I don't sell any squirrel"
|
||||||
msgstr "Je ne vends pas d'écureuil"
|
msgstr "Je ne vends pas d'écureuil"
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:55
|
#: squirrelbattle/entities/friendly.py:68
|
||||||
|
msgid "You have opened the chest"
|
||||||
|
msgstr "Vous avez ouvert le coffre"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:77
|
||||||
|
msgid "The chest exploded"
|
||||||
|
msgstr "Le coffre a explosé"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:78
|
||||||
|
msgid "It's not really effective"
|
||||||
|
msgstr "Ce n'est pas très efficace"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/friendly.py:101
|
||||||
msgid "Flower power!!"
|
msgid "Flower power!!"
|
||||||
msgstr "Pouvoir des fleurs !!"
|
msgstr "Pouvoir des fleurs !!"
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:55
|
#: squirrelbattle/entities/friendly.py:101
|
||||||
msgid "The sun is warm today"
|
msgid "The sun is warm today"
|
||||||
msgstr "Le soleil est chaud aujourd'hui"
|
msgstr "Le soleil est chaud aujourd'hui"
|
||||||
|
|
||||||
#. The bomb is exploding.
|
#. The bomb is exploding.
|
||||||
#. Each entity that is close to the bomb takes damages.
|
#. Each entity that is close to the bomb takes damages.
|
||||||
#. The player earn XP if the entity was killed.
|
#. The player earn XP if the entity was killed.
|
||||||
#: squirrelbattle/entities/items.py:178
|
#: squirrelbattle/entities/items.py:189
|
||||||
msgid "Bomb is exploding."
|
msgid "Bomb is exploding."
|
||||||
msgstr "La bombe explose."
|
msgstr "La bombe explose."
|
||||||
|
|
||||||
#: squirrelbattle/entities/items.py:365
|
#: squirrelbattle/entities/items.py:385
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{player} exchanged its body with {entity}."
|
msgid "{player} exchanged its body with {entity}."
|
||||||
msgstr "{player} a échangé son corps avec {entity}."
|
msgstr "{player} a échangé son corps avec {entity}."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:200
|
#: squirrelbattle/entities/items.py:519
|
||||||
|
msgid ""
|
||||||
|
"The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 "
|
||||||
|
"turns"
|
||||||
|
msgstr ""
|
||||||
|
"Les ennemis ont -{max(1, self.held_by.intelligence // 2)} de force pour 3 "
|
||||||
|
"tours"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:552
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name}"
|
||||||
|
msgstr "{name}"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:600
|
||||||
|
msgid " is shot by an arrow."
|
||||||
|
msgstr " est frappé par une flèche."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/items.py:622
|
||||||
|
msgid " is shot by a fire ball."
|
||||||
|
msgstr " est frappé par une boule de feu."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:83
|
||||||
|
msgid "It worked! Nearby ennemies will be confused for 3 turns."
|
||||||
|
msgstr "Ça a marché ! Les ennemis proches seront confus pendant 3 tours"
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:86
|
||||||
|
msgid "It worked, but there is no one nearby..."
|
||||||
|
msgstr "Ça a marché, mais il n'y a personne à proximité..."
|
||||||
|
|
||||||
|
#: squirrelbattle/entities/player.py:89
|
||||||
|
msgid "The dance was not effective..."
|
||||||
|
msgstr "La dance n'a pas fonctionné..."
|
||||||
|
|
||||||
|
#: squirrelbattle/game.py:214
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The player climbs down to the floor {floor}."
|
msgid "The player climbs down to the floor {floor}."
|
||||||
msgstr "Le joueur descend à l'étage {floor}."
|
msgstr "Le joueur descend à l'étage {floor}."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:213
|
#: squirrelbattle/game.py:227
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "The player climbs up the floor {floor}."
|
msgid "The player climbs up the floor {floor}."
|
||||||
msgstr "Le joueur monte à l'étage {floor}."
|
msgstr "Le joueur monte à l'étage {floor}."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603
|
#: squirrelbattle/game.py:348 squirrelbattle/tests/game_test.py:631
|
||||||
msgid "The buyer does not have enough money"
|
msgid "The buyer does not have enough money"
|
||||||
msgstr "L'acheteur n'a pas assez d'argent"
|
msgstr "L'acheteur n'a pas assez d'argent"
|
||||||
|
|
||||||
#: squirrelbattle/game.py:349
|
#: squirrelbattle/game.py:423
|
||||||
msgid ""
|
msgid ""
|
||||||
"Some keys are missing in your save file.\n"
|
"Some keys are missing in your save file.\n"
|
||||||
"Your save seems to be corrupt. It got deleted."
|
"Your save seems to be corrupt. It got deleted."
|
||||||
|
@ -128,7 +177,7 @@ msgstr ""
|
||||||
"Certaines clés de votre ficher de sauvegarde sont manquantes.\n"
|
"Certaines clés de votre ficher de sauvegarde sont manquantes.\n"
|
||||||
"Votre sauvegarde semble corrompue. Elle a été supprimée."
|
"Votre sauvegarde semble corrompue. Elle a été supprimée."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:357
|
#: squirrelbattle/game.py:431
|
||||||
msgid ""
|
msgid ""
|
||||||
"No player was found on this map!\n"
|
"No player was found on this map!\n"
|
||||||
"Maybe you died?"
|
"Maybe you died?"
|
||||||
|
@ -136,7 +185,7 @@ msgstr ""
|
||||||
"Aucun joueur n'a été trouvé sur la carte !\n"
|
"Aucun joueur n'a été trouvé sur la carte !\n"
|
||||||
"Peut-être êtes-vous mort ?"
|
"Peut-être êtes-vous mort ?"
|
||||||
|
|
||||||
#: squirrelbattle/game.py:379
|
#: squirrelbattle/game.py:454
|
||||||
msgid ""
|
msgid ""
|
||||||
"The JSON file is not correct.\n"
|
"The JSON file is not correct.\n"
|
||||||
"Your save seems corrupted. It got deleted."
|
"Your save seems corrupted. It got deleted."
|
||||||
|
@ -144,26 +193,31 @@ msgstr ""
|
||||||
"Le fichier JSON de sauvegarde est incorrect.\n"
|
"Le fichier JSON de sauvegarde est incorrect.\n"
|
||||||
"Votre sauvegarde semble corrompue. Elle a été supprimée."
|
"Votre sauvegarde semble corrompue. Elle a été supprimée."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:718
|
#: squirrelbattle/interfaces.py:758 squirrelbattle/tests/game_test.py:264
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} is confused, it can not hit {opponent}."
|
||||||
|
msgstr "{name} est confus et ne peut pas frapper {opponent}."
|
||||||
|
|
||||||
|
#: squirrelbattle/interfaces.py:766
|
||||||
msgid "It's a critical hit!"
|
msgid "It's a critical hit!"
|
||||||
msgstr "C'est un coup critique !"
|
msgstr "C'est un coup critique !"
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:719
|
#: squirrelbattle/interfaces.py:767
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} hits {opponent}."
|
msgid "{name} hits {opponent}."
|
||||||
msgstr "{name} frappe {opponent}."
|
msgstr "{name} frappe {opponent}."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:733
|
#: squirrelbattle/interfaces.py:782
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} takes {damage} damage."
|
msgid "{name} takes {damage} damage."
|
||||||
msgstr "{name} prend {damage} dégâts."
|
msgstr "{name} prend {damage} dégâts."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:735
|
#: squirrelbattle/interfaces.py:784
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{name} dies."
|
msgid "{name} dies."
|
||||||
msgstr "{name} meurt."
|
msgstr "{name} meurt."
|
||||||
|
|
||||||
#: squirrelbattle/interfaces.py:769
|
#: squirrelbattle/interfaces.py:818
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{entity} said: {message}"
|
msgid "{entity} said: {message}"
|
||||||
msgstr "{entity} a dit : {message}"
|
msgstr "{entity} a dit : {message}"
|
||||||
|
@ -172,8 +226,8 @@ msgstr "{entity} a dit : {message}"
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr "Retour"
|
msgstr "Retour"
|
||||||
|
|
||||||
#: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371
|
#: squirrelbattle/tests/game_test.py:395 squirrelbattle/tests/game_test.py:398
|
||||||
#: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377
|
#: squirrelbattle/tests/game_test.py:401 squirrelbattle/tests/game_test.py:404
|
||||||
#: squirrelbattle/tests/translations_test.py:16
|
#: squirrelbattle/tests/translations_test.py:16
|
||||||
msgid "New game"
|
msgid "New game"
|
||||||
msgstr "Nouvelle partie"
|
msgstr "Nouvelle partie"
|
||||||
|
@ -263,85 +317,113 @@ msgid "Key used to use ladders"
|
||||||
msgstr "Touche pour utiliser les échelles"
|
msgstr "Touche pour utiliser les échelles"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:58
|
#: squirrelbattle/tests/translations_test.py:58
|
||||||
|
msgid "Key used to use a bow"
|
||||||
|
msgstr "Touche pour utiliser un arc"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:60
|
||||||
|
msgid "Key used to dance"
|
||||||
|
msgstr "Touche pour danser"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:62
|
||||||
msgid "Texture pack"
|
msgid "Texture pack"
|
||||||
msgstr "Pack de textures"
|
msgstr "Pack de textures"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:59
|
#: squirrelbattle/tests/translations_test.py:63
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Langue"
|
msgstr "Langue"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:62
|
#: squirrelbattle/tests/translations_test.py:66
|
||||||
msgid "player"
|
msgid "player"
|
||||||
msgstr "joueur"
|
msgstr "joueur"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:64
|
#: squirrelbattle/tests/translations_test.py:68
|
||||||
msgid "hedgehog"
|
msgid "hedgehog"
|
||||||
msgstr "hérisson"
|
msgstr "hérisson"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:65
|
#: squirrelbattle/tests/translations_test.py:69
|
||||||
msgid "merchant"
|
msgid "merchant"
|
||||||
msgstr "marchand"
|
msgstr "marchand"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:66
|
#: squirrelbattle/tests/translations_test.py:70
|
||||||
msgid "rabbit"
|
msgid "rabbit"
|
||||||
msgstr "lapin"
|
msgstr "lapin"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:67
|
#: squirrelbattle/tests/translations_test.py:71
|
||||||
msgid "sunflower"
|
msgid "sunflower"
|
||||||
msgstr "tournesol"
|
msgstr "tournesol"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:68
|
#: squirrelbattle/tests/translations_test.py:72
|
||||||
msgid "teddy bear"
|
msgid "teddy bear"
|
||||||
msgstr "nounours"
|
msgstr "nounours"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:69
|
#: squirrelbattle/tests/translations_test.py:73
|
||||||
msgid "tiger"
|
msgid "tiger"
|
||||||
msgstr "tigre"
|
msgstr "tigre"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:70
|
#: squirrelbattle/tests/translations_test.py:74
|
||||||
msgid "eagle"
|
msgid "eagle"
|
||||||
msgstr "pygargue"
|
msgstr "pygargue"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:72
|
#: squirrelbattle/tests/translations_test.py:76
|
||||||
msgid "body snatch potion"
|
msgid "body snatch potion"
|
||||||
msgstr "potion d'arrachage de corps"
|
msgstr "potion d'arrachage de corps"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:73
|
#: squirrelbattle/tests/translations_test.py:77
|
||||||
msgid "bomb"
|
msgid "bomb"
|
||||||
msgstr "bombe"
|
msgstr "bombe"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:74
|
#: squirrelbattle/tests/translations_test.py:78
|
||||||
msgid "explosion"
|
msgid "explosion"
|
||||||
msgstr "explosion"
|
msgstr "explosion"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:75
|
#: squirrelbattle/tests/translations_test.py:79
|
||||||
msgid "heart"
|
msgid "heart"
|
||||||
msgstr "cœur"
|
msgstr "cœur"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:76
|
#: squirrelbattle/tests/translations_test.py:80
|
||||||
msgid "sword"
|
msgid "sword"
|
||||||
msgstr "épée"
|
msgstr "épée"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:77
|
#: squirrelbattle/tests/translations_test.py:81
|
||||||
msgid "helmet"
|
msgid "helmet"
|
||||||
msgstr "casque"
|
msgstr "casque"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:78
|
#: squirrelbattle/tests/translations_test.py:82
|
||||||
msgid "chestplate"
|
msgid "chestplate"
|
||||||
msgstr "plastron"
|
msgstr "plastron"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:79
|
#: squirrelbattle/tests/translations_test.py:83
|
||||||
msgid "shield"
|
msgid "shield"
|
||||||
msgstr "bouclier"
|
msgstr "bouclier"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:80
|
#: squirrelbattle/tests/translations_test.py:84
|
||||||
|
msgid "ruler"
|
||||||
|
msgstr "règle"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:85
|
||||||
|
msgid "scroll of damage"
|
||||||
|
msgstr "parchemin de dégâts"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:86
|
||||||
|
msgid "scroll of weakness"
|
||||||
|
msgstr "parchemin de faiblesse"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:87
|
||||||
|
msgid "bow"
|
||||||
|
msgstr "arc"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:88
|
||||||
|
msgid "fire ball staff"
|
||||||
|
msgstr "baton de boule de feu"
|
||||||
|
|
||||||
|
#: squirrelbattle/tests/translations_test.py:89
|
||||||
msgid "ring of critical damage"
|
msgid "ring of critical damage"
|
||||||
msgstr "anneau de coup critique"
|
msgstr "anneau de coup critique"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:82
|
#: squirrelbattle/tests/translations_test.py:91
|
||||||
msgid "ring of more experience"
|
msgid "ring of more experience"
|
||||||
msgstr "anneau de plus d'expérience"
|
msgstr "anneau de plus d'expérience"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:84
|
#: squirrelbattle/tests/translations_test.py:93
|
||||||
msgid "monocle"
|
msgid "monocle"
|
||||||
msgstr "monocle"
|
msgstr "monocle"
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from random import random, randint, shuffle, choice, choices
|
from random import choice, choices, randint, random, shuffle
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
from ..interfaces import Map, Tile, Entity
|
from ..interfaces import Entity, Map, Tile
|
||||||
|
|
||||||
DEFAULT_PARAMS = {
|
DEFAULT_PARAMS = {
|
||||||
"width": 120,
|
"width": 120,
|
||||||
|
@ -20,10 +20,10 @@ DEFAULT_PARAMS = {
|
||||||
"max_h_corr": 12,
|
"max_h_corr": 12,
|
||||||
"large_circular_room": .10,
|
"large_circular_room": .10,
|
||||||
"circular_holes": .5,
|
"circular_holes": .5,
|
||||||
"loop_tries" : 40,
|
"loop_tries": 40,
|
||||||
"loop_max" : 5,
|
"loop_max": 5,
|
||||||
"loop_threshold" : 15,
|
"loop_threshold": 15,
|
||||||
"spawn_per_region" : [1, 2],
|
"spawn_per_region": [1, 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
def dist(level, y1, x1, y2, x2):
|
def dist(level, y1, x1, y2, x2):
|
||||||
|
@ -31,7 +31,7 @@ def dist(level, y1, x1, y2, x2):
|
||||||
Compute the minimum walking distance between points (y1, x1) and (y2, x2) on a Tile grid
|
Compute the minimum walking distance between points (y1, x1) and (y2, x2) on a Tile grid
|
||||||
"""
|
"""
|
||||||
# simple breadth first search
|
# simple breadth first search
|
||||||
copy = [[t for t in l] for l in level]
|
copy = [[t for t in row] for row in level]
|
||||||
dist = -1
|
dist = -1
|
||||||
queue, next_queue = [[y1, x1]], [0]
|
queue, next_queue = [[y1, x1]], [0]
|
||||||
while next_queue:
|
while next_queue:
|
||||||
|
@ -48,6 +48,7 @@ def dist(level, y1, x1, y2, x2):
|
||||||
queue = next_queue
|
queue = next_queue
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
class Generator:
|
class Generator:
|
||||||
def __init__(self, params: dict = None):
|
def __init__(self, params: dict = None):
|
||||||
self.params = params or DEFAULT_PARAMS
|
self.params = params or DEFAULT_PARAMS
|
||||||
|
@ -104,7 +105,7 @@ class Generator:
|
||||||
level[y - door_y + ry][x - door_x + rx] = Tile.FLOOR
|
level[y - door_y + ry][x - door_x + rx] = Tile.FLOOR
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_loop(level: List[List[Tile]], y: int, x: int) -> None:
|
def add_loop(level: List[List[Tile]], y: int, x: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Try to add a corridor between two far apart floor tiles, passing
|
Try to add a corridor between two far apart floor tiles, passing
|
||||||
through point (y, x).
|
through point (y, x).
|
||||||
|
@ -127,23 +128,26 @@ class Generator:
|
||||||
if not(0 <= x1 <= x2 < w and 0 <= y1 <= y2 < h):
|
if not(0 <= x1 <= x2 < w and 0 <= y1 <= y2 < h):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def verify_sides():
|
def verify_sides() -> bool:
|
||||||
# switching up dy and dx here pivots the axis, so
|
# switching up dy and dx here pivots the axis, so
|
||||||
# (y+dx, x+dy) and (y-dx, x-dy) are the tiles adjacent to
|
# (y+dx, x+dy) and (y-dx, x-dy) are the tiles adjacent to
|
||||||
# (y, x), but not on the original axis
|
# (y, x), but not on the original axis
|
||||||
for Dx, Dy in [[dy, dx], [-dy, -dx]]:
|
for delta_x, delta_y in [[dy, dx], [-dy, -dx]]:
|
||||||
for i in range(1, y2-y1+x2-x1):
|
for i in range(1, y2 - y1 + x2 - x1):
|
||||||
if not(0<= y1+Dy+i*dy < h and 0 <= x1+Dx+i*dx < w) or \
|
if not (0 <= y1 + delta_y + i * dy < h
|
||||||
level[y1+Dy+i*dy][x1+Dx+i*dx].can_walk():
|
and 0 <= x1 + delta_x + i * dx < w) or \
|
||||||
|
level[y1 + delta_y + i * dy][x1 + delta_x
|
||||||
|
+ i * dx]\
|
||||||
|
.can_walk():
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
# if adding the path would make the two tiles significantly closer
|
# if adding the path would make the two tiles significantly closer
|
||||||
# and its sides don't touch already placed terrain, build it
|
# and its sides don't touch already placed terrain, build it
|
||||||
if dist(level, y1, x1, y2, x2) < 20 and verify_sides():
|
if dist(level, y1, x1, y2, x2) < 20 and verify_sides():
|
||||||
y, x = y1+dy, x1+dx
|
y, x = y1 + dy, x1 + dx
|
||||||
while level[y][x] == Tile.EMPTY:
|
while level[y][x] == Tile.EMPTY:
|
||||||
level[y][x] = Tile.FLOOR
|
level[y][x] = Tile.FLOOR
|
||||||
y, x = y+dy, x+dx
|
y, x = y + dy, x + dx
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -187,7 +191,8 @@ class Generator:
|
||||||
return 0, 0, 0, 0
|
return 0, 0, 0, 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def build_door(room, y, x, dy, dx, length):
|
def build_door(room: List[List[Tile]], y: int, x: int,
|
||||||
|
dy: int, dx: int, length: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Tries to build the exit from the room at given coordinates
|
Tries to build the exit from the room at given coordinates
|
||||||
Depending on parameter length, it will either attempt to build a
|
Depending on parameter length, it will either attempt to build a
|
||||||
|
@ -206,7 +211,7 @@ class Generator:
|
||||||
and room[ny][nx] != Tile.EMPTY:
|
and room[ny][nx] != Tile.EMPTY:
|
||||||
return False
|
return False
|
||||||
# see if the path ahead is clear. needed in the case of non convex room
|
# see if the path ahead is clear. needed in the case of non convex room
|
||||||
for i in range(length+1):
|
for i in range(length + 1):
|
||||||
if room[y + i * dy][x + i * dx] != Tile.EMPTY:
|
if room[y + i * dy][x + i * dx] != Tile.EMPTY:
|
||||||
return False
|
return False
|
||||||
for i in range(length):
|
for i in range(length):
|
||||||
|
@ -300,7 +305,7 @@ class Generator:
|
||||||
"""
|
"""
|
||||||
return self.create_circular_room()
|
return self.create_circular_room()
|
||||||
|
|
||||||
def register_spawn_area(self, area:List[List[Tile]]):
|
def register_spawn_area(self, area: List[List[Tile]]) -> None:
|
||||||
"""
|
"""
|
||||||
Register all floor positions relative to the input grid
|
Register all floor positions relative to the input grid
|
||||||
for later use
|
for later use
|
||||||
|
@ -312,7 +317,7 @@ class Generator:
|
||||||
spawn_positions.append([y, x])
|
spawn_positions.append([y, x])
|
||||||
self.queued_area = spawn_positions
|
self.queued_area = spawn_positions
|
||||||
|
|
||||||
def update_spawnable(self, y, x):
|
def update_spawnable(self, y: int, x: int) -> None:
|
||||||
"""
|
"""
|
||||||
Convert previous spawn positions relative to the room grid to actual
|
Convert previous spawn positions relative to the room grid to actual
|
||||||
actual spawn positions on the level grid, using the position of the
|
actual spawn positions on the level grid, using the position of the
|
||||||
|
@ -324,11 +329,16 @@ class Generator:
|
||||||
self.spawn_areas.append(translated_area)
|
self.spawn_areas.append(translated_area)
|
||||||
self.queued_area = None
|
self.queued_area = None
|
||||||
|
|
||||||
def populate(self, rv):
|
def populate(self, rv: Map) -> None:
|
||||||
"""
|
"""
|
||||||
Populate every spawnable area with some randomly chosen, randomly
|
Populate every spawnable area with some randomly chosen, randomly
|
||||||
placed entity
|
placed entity
|
||||||
"""
|
"""
|
||||||
|
if self.queued_area is not None:
|
||||||
|
translated_area = [[y + ry, x + rx] for ry, rx in self.queued_area]
|
||||||
|
self.spawn_areas.append(translated_area)
|
||||||
|
self.queued_area = None
|
||||||
|
|
||||||
min_c, max_c = self.params["spawn_per_region"]
|
min_c, max_c = self.params["spawn_per_region"]
|
||||||
for region in self.spawn_areas:
|
for region in self.spawn_areas:
|
||||||
entity_count = randint(min_c, max_c)
|
entity_count = randint(min_c, max_c)
|
||||||
|
@ -349,7 +359,7 @@ class Generator:
|
||||||
|
|
||||||
# the starting room must have no corridor
|
# the starting room must have no corridor
|
||||||
mem, self.params["corridor_chance"] = self.params["corridor_chance"], 0
|
mem, self.params["corridor_chance"] = self.params["corridor_chance"], 0
|
||||||
starting_room, _, _, _, _ = self.create_random_room(spawnable = False)
|
starting_room, _, _, _, _ = self.create_random_room(spawnable=False)
|
||||||
dim_v, dim_h = len(starting_room), len(starting_room[0])
|
dim_v, dim_h = len(starting_room), len(starting_room[0])
|
||||||
# because Generator.room_fits checks that the exit door is correctly
|
# because Generator.room_fits checks that the exit door is correctly
|
||||||
# placed, but the starting room has no exit door, we find a positoin
|
# placed, but the starting room has no exit door, we find a positoin
|
||||||
|
@ -399,7 +409,7 @@ class Generator:
|
||||||
while tries < self.params["loop_tries"] and \
|
while tries < self.params["loop_tries"] and \
|
||||||
loops < self.params["loop_max"]:
|
loops < self.params["loop_max"]:
|
||||||
tries += 1
|
tries += 1
|
||||||
y, x = randint(0, height-1), randint(0, width-1)
|
y, x = randint(0, height - 1), randint(0, width - 1)
|
||||||
loops += self.add_loop(level, y, x)
|
loops += self.add_loop(level, y, x)
|
||||||
|
|
||||||
# place an exit ladder
|
# place an exit ladder
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from .display.texturepack import TexturePack
|
from .display.texturepack import TexturePack
|
||||||
|
from .entities.friendly import Chest, Merchant
|
||||||
from .entities.player import Player
|
from .entities.player import Player
|
||||||
from .entities.friendly import Merchant
|
from .enums import DisplayActions, GameMode, KeyValues
|
||||||
from .enums import GameMode, KeyValues, DisplayActions
|
|
||||||
from .settings import Settings
|
from .settings import Settings
|
||||||
from .translations import gettext as _, Translator
|
from .translations import gettext as _, Translator
|
||||||
|
|
||||||
|
@ -158,3 +158,23 @@ class StoreMenu(Menu):
|
||||||
Returns the values of the menu.
|
Returns the values of the menu.
|
||||||
"""
|
"""
|
||||||
return self.merchant.inventory if self.merchant else []
|
return self.merchant.inventory if self.merchant else []
|
||||||
|
|
||||||
|
|
||||||
|
class ChestMenu(Menu):
|
||||||
|
"""
|
||||||
|
A special instance of a menu : the menu for the inventory of a chest.
|
||||||
|
"""
|
||||||
|
chest: Chest = None
|
||||||
|
|
||||||
|
def update_chest(self, chest: Chest) -> None:
|
||||||
|
"""
|
||||||
|
Updates the player.
|
||||||
|
"""
|
||||||
|
self.chest = chest
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self) -> list:
|
||||||
|
"""
|
||||||
|
Returns the values of the menu.
|
||||||
|
"""
|
||||||
|
return self.chest.inventory if self.chest else []
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
@ -35,6 +35,8 @@ class Settings:
|
||||||
self.KEY_CHAT = ['t', 'Key used to talk to a friendly entity']
|
self.KEY_CHAT = ['t', 'Key used to talk to a friendly entity']
|
||||||
self.KEY_WAIT = ['w', 'Key used to wait']
|
self.KEY_WAIT = ['w', 'Key used to wait']
|
||||||
self.KEY_LADDER = ['<', 'Key used to use ladders']
|
self.KEY_LADDER = ['<', 'Key used to use ladders']
|
||||||
|
self.KEY_LAUNCH = ['l', 'Key used to use a bow']
|
||||||
|
self.KEY_DANCE = ['y', 'Key used to dance']
|
||||||
self.TEXTURE_PACK = ['ascii', 'Texture pack']
|
self.TEXTURE_PACK = ['ascii', 'Texture pack']
|
||||||
self.LOCALE = [locale.getlocale()[0][:2], 'Language']
|
self.LOCALE = [locale.getlocale()[0][:2], 'Language']
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import random
|
import random
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart, Item, \
|
from ..entities.friendly import Chest, Trumpet
|
||||||
Explosion
|
from ..entities.items import BodySnatchPotion, Bomb, Explosion, Heart, Item
|
||||||
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit,\
|
from ..entities.monsters import GiantSeaEagle, Hedgehog, Rabbit, \
|
||||||
TeddyBear, GiantSeaEagle
|
TeddyBear, Tiger
|
||||||
from squirrelbattle.entities.friendly import Trumpet
|
from ..entities.player import Player
|
||||||
from squirrelbattle.entities.player import Player
|
from ..interfaces import Entity, Map
|
||||||
from squirrelbattle.interfaces import Entity, Map
|
from ..resources import ResourceManager
|
||||||
from squirrelbattle.resources import ResourceManager
|
|
||||||
|
|
||||||
|
|
||||||
class TestEntities(unittest.TestCase):
|
class TestEntities(unittest.TestCase):
|
||||||
|
@ -45,18 +45,19 @@ class TestEntities(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
entity = Tiger()
|
entity = Tiger()
|
||||||
self.map.add_entity(entity)
|
self.map.add_entity(entity)
|
||||||
self.assertEqual(entity.maxhealth, 20)
|
self.assertEqual(entity.maxhealth, 30)
|
||||||
self.assertEqual(entity.maxhealth, entity.health)
|
self.assertEqual(entity.maxhealth, entity.health)
|
||||||
self.assertEqual(entity.strength, 2)
|
self.assertEqual(entity.strength, 5)
|
||||||
for _ in range(9):
|
for _ in range(5):
|
||||||
self.assertEqual(entity.hit(entity),
|
self.assertEqual(entity.hit(entity),
|
||||||
"Tiger hits tiger. Tiger takes 2 damage.")
|
"Tiger hits tiger. Tiger takes 5 damage.")
|
||||||
self.assertFalse(entity.dead)
|
self.assertFalse(entity.dead)
|
||||||
self.assertEqual(entity.hit(entity), "Tiger hits tiger. "
|
self.assertEqual(entity.hit(entity), "Tiger hits tiger. "
|
||||||
+ "Tiger takes 2 damage. Tiger dies.")
|
+ "Tiger takes 5 damage. Tiger dies.")
|
||||||
self.assertTrue(entity.dead)
|
self.assertTrue(entity.dead)
|
||||||
|
|
||||||
entity = Rabbit()
|
entity = Rabbit()
|
||||||
|
entity.health = 15
|
||||||
entity.critical = 0
|
entity.critical = 0
|
||||||
self.map.add_entity(entity)
|
self.map.add_entity(entity)
|
||||||
entity.move(15, 44)
|
entity.move(15, 44)
|
||||||
|
@ -94,7 +95,20 @@ class TestEntities(unittest.TestCase):
|
||||||
self.assertTrue(entity.dead)
|
self.assertTrue(entity.dead)
|
||||||
self.assertGreaterEqual(self.player.current_xp, 3)
|
self.assertGreaterEqual(self.player.current_xp, 3)
|
||||||
|
|
||||||
# Test the familiars
|
# Test that a chest is destroyed by a bomb
|
||||||
|
bomb = Bomb()
|
||||||
|
bomb.owner = self.player
|
||||||
|
bomb.move(3, 6)
|
||||||
|
self.map.add_entity(bomb)
|
||||||
|
chest = Chest()
|
||||||
|
chest.move(4, 6)
|
||||||
|
self.map.add_entity(chest)
|
||||||
|
bomb.exploding = True
|
||||||
|
for _ in range(5):
|
||||||
|
self.map.tick(self.player)
|
||||||
|
self.assertTrue(chest.annihilated)
|
||||||
|
|
||||||
|
def test_familiar(self) -> None:
|
||||||
fam = Trumpet()
|
fam = Trumpet()
|
||||||
entity = Rabbit()
|
entity = Rabbit()
|
||||||
self.map.add_entity(entity)
|
self.map.add_entity(entity)
|
||||||
|
@ -266,6 +280,15 @@ class TestEntities(unittest.TestCase):
|
||||||
player_state = player.save_state()
|
player_state = player.save_state()
|
||||||
self.assertEqual(player_state["current_xp"], 10)
|
self.assertEqual(player_state["current_xp"], 10)
|
||||||
|
|
||||||
|
player = Player()
|
||||||
|
player.map = self.map
|
||||||
|
player.add_xp(700)
|
||||||
|
for _ in range(13):
|
||||||
|
player.level_up()
|
||||||
|
self.assertEqual(player.level, 12)
|
||||||
|
self.assertEqual(player.critical, 5 + 95 // 30)
|
||||||
|
self.assertEqual(player.charisma, 3)
|
||||||
|
|
||||||
def test_critical_hit(self) -> None:
|
def test_critical_hit(self) -> None:
|
||||||
"""
|
"""
|
||||||
Ensure that critical hits are working.
|
Ensure that critical hits are working.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
|
@ -8,13 +8,14 @@ import unittest
|
||||||
from ..bootstrap import Bootstrap
|
from ..bootstrap import Bootstrap
|
||||||
from ..display.display import Display
|
from ..display.display import Display
|
||||||
from ..display.display_manager import DisplayManager
|
from ..display.display_manager import DisplayManager
|
||||||
from ..entities.friendly import Merchant, Sunflower
|
from ..entities.friendly import Chest, Merchant, Sunflower
|
||||||
from ..entities.items import Bomb, Heart, Sword, Explosion, Shield, Helmet, \
|
from ..entities.items import Bomb, Bow, Chestplate, Explosion, FireBallStaff, \
|
||||||
Chestplate, RingCritical, Monocle
|
Heart, Helmet, Monocle, RingCritical, ScrollofDamage, ScrollofWeakening, \
|
||||||
from ..entities.monsters import GiantSeaEagle
|
Shield, Sword
|
||||||
|
from ..entities.monsters import GiantSeaEagle, Rabbit
|
||||||
from ..entities.player import Player
|
from ..entities.player import Player
|
||||||
from ..enums import DisplayActions
|
from ..enums import DisplayActions, GameMode, KeyValues
|
||||||
from ..game import Game, KeyValues, GameMode
|
from ..game import Game
|
||||||
from ..interfaces import Map, Tile
|
from ..interfaces import Map, Tile
|
||||||
from ..menus import MainMenuValues
|
from ..menus import MainMenuValues
|
||||||
from ..resources import ResourceManager
|
from ..resources import ResourceManager
|
||||||
|
@ -159,6 +160,9 @@ class TestGame(unittest.TestCase):
|
||||||
KeyValues.SPACE)
|
KeyValues.SPACE)
|
||||||
self.assertEqual(KeyValues.translate_key('plop', self.game.settings),
|
self.assertEqual(KeyValues.translate_key('plop', self.game.settings),
|
||||||
None)
|
None)
|
||||||
|
self.assertEqual(KeyValues.translate_key(
|
||||||
|
self.game.settings.KEY_DANCE, self.game.settings),
|
||||||
|
KeyValues.DANCE)
|
||||||
|
|
||||||
def test_key_press(self) -> None:
|
def test_key_press(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -254,6 +258,28 @@ class TestGame(unittest.TestCase):
|
||||||
self.game.handle_key_pressed(KeyValues.WAIT)
|
self.game.handle_key_pressed(KeyValues.WAIT)
|
||||||
self.assertNotIn(explosion, self.game.map.entities)
|
self.assertNotIn(explosion, self.game.map.entities)
|
||||||
|
|
||||||
|
rabbit = Rabbit()
|
||||||
|
self.game.map.add_entity(rabbit)
|
||||||
|
self.game.player.move(1, 6)
|
||||||
|
rabbit.move(3, 6)
|
||||||
|
self.game.player.charisma = 11
|
||||||
|
self.game.handle_key_pressed(KeyValues.DANCE)
|
||||||
|
self.assertEqual(rabbit.confused, 1)
|
||||||
|
string = rabbit.hit(self.game.player)
|
||||||
|
self.assertEqual(
|
||||||
|
string, _("{name} is confused, it can not hit {opponent}.")
|
||||||
|
.format(name=rabbit.translated_name.capitalize(),
|
||||||
|
opponent=self.game.player.translated_name))
|
||||||
|
rabbit.confused = 0
|
||||||
|
self.game.player.charisma = 0
|
||||||
|
self.game.handle_key_pressed(KeyValues.DANCE)
|
||||||
|
self.assertEqual(rabbit.confused, 0)
|
||||||
|
rabbit.die()
|
||||||
|
|
||||||
|
self.game.player.charisma = 11
|
||||||
|
self.game.handle_key_pressed(KeyValues.DANCE)
|
||||||
|
self.game.player.charisma = 1
|
||||||
|
|
||||||
self.game.handle_key_pressed(KeyValues.SPACE)
|
self.game.handle_key_pressed(KeyValues.SPACE)
|
||||||
self.assertEqual(self.game.state, GameMode.MAINMENU)
|
self.assertEqual(self.game.state, GameMode.MAINMENU)
|
||||||
|
|
||||||
|
@ -355,7 +381,7 @@ class TestGame(unittest.TestCase):
|
||||||
self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a')
|
self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a')
|
||||||
|
|
||||||
# Navigate to "texture pack"
|
# Navigate to "texture pack"
|
||||||
for ignored in range(12):
|
for ignored in range(14):
|
||||||
self.game.handle_key_pressed(KeyValues.DOWN)
|
self.game.handle_key_pressed(KeyValues.DOWN)
|
||||||
|
|
||||||
# Change texture pack
|
# Change texture pack
|
||||||
|
@ -771,3 +797,157 @@ class TestGame(unittest.TestCase):
|
||||||
self.game.handle_key_pressed(KeyValues.ENTER)
|
self.game.handle_key_pressed(KeyValues.ENTER)
|
||||||
|
|
||||||
self.assertEqual(self.game.state, GameMode.MAINMENU)
|
self.assertEqual(self.game.state, GameMode.MAINMENU)
|
||||||
|
|
||||||
|
def test_launch(self) -> None:
|
||||||
|
"""
|
||||||
|
Use the long range weapons to kill some entities.
|
||||||
|
"""
|
||||||
|
self.game.state = GameMode.PLAY
|
||||||
|
self.game.player.move(2, 6)
|
||||||
|
|
||||||
|
b = Bow()
|
||||||
|
b.held_by = self.game.player
|
||||||
|
self.game.player.equipped_main = b
|
||||||
|
self.assertTrue(self.game.player.equipped_main)
|
||||||
|
|
||||||
|
entity = Rabbit()
|
||||||
|
entity.health = 1
|
||||||
|
self.game.map.add_entity(entity)
|
||||||
|
entity.move(3, 6)
|
||||||
|
|
||||||
|
self.game.handle_launch(KeyValues.UP)
|
||||||
|
|
||||||
|
self.game.waiting_for_launch_key = True
|
||||||
|
self.game.handle_key_pressed(KeyValues.CHAT)
|
||||||
|
|
||||||
|
entity = Rabbit()
|
||||||
|
entity.health = 1
|
||||||
|
self.game.map.add_entity(entity)
|
||||||
|
entity.move(2, 8)
|
||||||
|
self.game.waiting_for_launch_key = True
|
||||||
|
self.game.handle_key_pressed(KeyValues.RIGHT)
|
||||||
|
|
||||||
|
entity = Rabbit()
|
||||||
|
entity.health = 1
|
||||||
|
self.game.map.add_entity(entity)
|
||||||
|
entity.move(2, 5)
|
||||||
|
self.game.waiting_for_launch_key = True
|
||||||
|
self.game.handle_key_pressed(KeyValues.LEFT)
|
||||||
|
|
||||||
|
key = "l"
|
||||||
|
KeyValues.translate_key(key, self.game.settings)
|
||||||
|
|
||||||
|
self.game.handle_key_pressed(KeyValues.LAUNCH)
|
||||||
|
self.assertTrue(self.game.waiting_for_launch_key)
|
||||||
|
self.game.handle_key_pressed(KeyValues.DOWN)
|
||||||
|
|
||||||
|
self.assertTrue(entity.dead)
|
||||||
|
|
||||||
|
entity2 = Rabbit()
|
||||||
|
entity2.health = 1
|
||||||
|
self.game.map.add_entity(entity2)
|
||||||
|
entity2.move(1, 6)
|
||||||
|
|
||||||
|
b = FireBallStaff()
|
||||||
|
self.game.player.inventory.append(b)
|
||||||
|
b.held_by = self.game.player
|
||||||
|
b.equip()
|
||||||
|
|
||||||
|
self.game.handle_key_pressed(KeyValues.LAUNCH)
|
||||||
|
self.assertTrue(self.game.waiting_for_launch_key)
|
||||||
|
self.game.handle_key_pressed(KeyValues.UP)
|
||||||
|
|
||||||
|
self.assertTrue(entity2.dead)
|
||||||
|
|
||||||
|
def test_scrolls(self) -> None:
|
||||||
|
"""
|
||||||
|
Use the scrolls.
|
||||||
|
"""
|
||||||
|
self.game.state = GameMode.PLAY
|
||||||
|
self.game.player.move(2, 6)
|
||||||
|
|
||||||
|
entity = Rabbit()
|
||||||
|
self.game.map.add_entity(entity)
|
||||||
|
entity.move(3, 6)
|
||||||
|
|
||||||
|
entity2 = GiantSeaEagle()
|
||||||
|
self.game.map.add_entity(entity2)
|
||||||
|
entity2.move(3, 8)
|
||||||
|
|
||||||
|
scroll1 = ScrollofDamage()
|
||||||
|
scroll2 = ScrollofWeakening()
|
||||||
|
self.game.player.inventory.append(scroll1)
|
||||||
|
self.game.player.inventory.append(scroll2)
|
||||||
|
scroll1.held_by = self.game.player
|
||||||
|
scroll2.held_by = self.game.player
|
||||||
|
|
||||||
|
scroll1.use()
|
||||||
|
self.assertTrue(entity.health != entity.maxhealth)
|
||||||
|
self.assertTrue(entity2.health != entity2.maxhealth)
|
||||||
|
|
||||||
|
scroll2.use()
|
||||||
|
self.assertEqual(entity.strength, 0)
|
||||||
|
self.assertEqual(entity2.strength, 999)
|
||||||
|
|
||||||
|
self.game.map.tick(self.game.player)
|
||||||
|
self.game.map.tick(self.game.player)
|
||||||
|
self.game.map.tick(self.game.player)
|
||||||
|
|
||||||
|
self.assertEqual(entity2.effects, [])
|
||||||
|
|
||||||
|
def test_chests(self) -> None:
|
||||||
|
"""
|
||||||
|
Interacts with chests.
|
||||||
|
"""
|
||||||
|
self.game.state = GameMode.PLAY
|
||||||
|
|
||||||
|
chest = Chest()
|
||||||
|
chest.move(2, 6)
|
||||||
|
self.game.map.add_entity(chest)
|
||||||
|
chest.inventory.append(FireBallStaff())
|
||||||
|
|
||||||
|
# Talk to merchant
|
||||||
|
self.game.handle_key_pressed(KeyValues.CHAT)
|
||||||
|
self.assertTrue(self.game.waiting_for_friendly_key)
|
||||||
|
self.game.handle_key_pressed(KeyValues.DOWN)
|
||||||
|
self.assertFalse(self.game.waiting_for_friendly_key)
|
||||||
|
self.assertEqual(self.game.state, GameMode.CHEST)
|
||||||
|
self.assertTrue(self.game.logs.messages)
|
||||||
|
|
||||||
|
# Navigate in the menu
|
||||||
|
self.game.handle_key_pressed(KeyValues.DOWN)
|
||||||
|
self.game.handle_key_pressed(KeyValues.DOWN)
|
||||||
|
self.game.handle_key_pressed(KeyValues.LEFT)
|
||||||
|
self.assertFalse(self.game.is_in_chest_menu)
|
||||||
|
self.game.handle_key_pressed(KeyValues.RIGHT)
|
||||||
|
self.assertTrue(self.game.is_in_chest_menu)
|
||||||
|
self.game.handle_key_pressed(KeyValues.UP)
|
||||||
|
self.assertEqual(self.game.chest_menu.position, 1)
|
||||||
|
|
||||||
|
# The second item is not a heart
|
||||||
|
chest.inventory[1] = sword = Sword()
|
||||||
|
# Take the second item
|
||||||
|
item = self.game.chest_menu.validate()
|
||||||
|
self.assertIn(item, chest.inventory)
|
||||||
|
self.game.display_actions(DisplayActions.MOUSE, 7, 25,
|
||||||
|
curses.BUTTON1_CLICKED)
|
||||||
|
self.assertIn(item, self.game.player.inventory)
|
||||||
|
self.assertNotIn(item, chest.inventory)
|
||||||
|
|
||||||
|
# Give an item back
|
||||||
|
self.game.inventory_menu.position = len(self.game.player.inventory) - 1
|
||||||
|
self.game.handle_key_pressed(KeyValues.LEFT)
|
||||||
|
self.assertFalse(self.game.is_in_chest_menu)
|
||||||
|
self.assertIn(sword, self.game.player.inventory)
|
||||||
|
self.assertEqual(self.game.inventory_menu.validate(), sword)
|
||||||
|
self.game.handle_key_pressed(KeyValues.ENTER)
|
||||||
|
self.assertNotIn(sword, self.game.player.inventory)
|
||||||
|
self.assertIn(sword, chest.inventory)
|
||||||
|
|
||||||
|
# Test immortality
|
||||||
|
self.game.player.hit(chest)
|
||||||
|
self.assertTrue(not chest.dead)
|
||||||
|
|
||||||
|
# Exit the menu
|
||||||
|
self.game.handle_key_pressed(KeyValues.SPACE)
|
||||||
|
self.assertEqual(self.game.state, GameMode.PLAY)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from squirrelbattle.display.texturepack import TexturePack
|
from ..display.texturepack import TexturePack
|
||||||
from squirrelbattle.interfaces import Map, Tile, Slope
|
from ..interfaces import Map, Slope, Tile
|
||||||
from squirrelbattle.resources import ResourceManager
|
from ..resources import ResourceManager
|
||||||
|
|
||||||
|
|
||||||
class TestInterfaces(unittest.TestCase):
|
class TestInterfaces(unittest.TestCase):
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import unittest
|
|
||||||
from random import randint
|
from random import randint
|
||||||
from typing import List
|
from typing import List
|
||||||
|
import unittest
|
||||||
|
|
||||||
from squirrelbattle.interfaces import Map, Tile
|
from ..display.texturepack import TexturePack
|
||||||
from squirrelbattle.mapgeneration import broguelike
|
from ..interfaces import Map, Tile
|
||||||
from squirrelbattle.display.texturepack import TexturePack
|
from ..mapgeneration import broguelike
|
||||||
|
|
||||||
|
|
||||||
class TestBroguelike(unittest.TestCase):
|
class TestBroguelike(unittest.TestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self.generator = broguelike.Generator()
|
self.generator = broguelike.Generator()
|
||||||
self.stom = lambda x : Map.load_from_string("0 0\n" + x)
|
self.stom = lambda x: Map.load_from_string("0 0\n" + x)
|
||||||
self.mtos = lambda x : x.draw_string(TexturePack.ASCII_PACK)
|
self.mtos = lambda x: x.draw_string(TexturePack.ASCII_PACK)
|
||||||
|
|
||||||
def test_dist(self):
|
def test_dist(self) -> None:
|
||||||
m = self.stom(".. ..\n ... ")
|
m = self.stom(".. ..\n ... ")
|
||||||
distance = broguelike.dist(m.tiles, 0, 0, 0, 4)
|
distance = broguelike.dist(m.tiles, 0, 0, 0, 4)
|
||||||
self.assertEqual(distance, 6)
|
self.assertEqual(distance, 6)
|
||||||
|
@ -37,7 +37,7 @@ class TestBroguelike(unittest.TestCase):
|
||||||
queue += Map.neighbourhood(grid, y, x)
|
queue += Map.neighbourhood(grid, y, x)
|
||||||
return not any([t.can_walk() for row in grid for t in row])
|
return not any([t.can_walk() for row in grid for t in row])
|
||||||
|
|
||||||
def test_build_doors(self):
|
def test_build_doors(self) -> None:
|
||||||
m = self.stom(". .\n. .\n. .\n")
|
m = self.stom(". .\n. .\n. .\n")
|
||||||
self.assertFalse(self.generator.build_door(m.tiles, 1, 1, 0, 1, 2))
|
self.assertFalse(self.generator.build_door(m.tiles, 1, 1, 0, 1, 2))
|
||||||
|
|
||||||
|
@ -46,11 +46,10 @@ class TestBroguelike(unittest.TestCase):
|
||||||
self.assertTrue(self.is_connex(m.tiles))
|
self.assertTrue(self.is_connex(m.tiles))
|
||||||
|
|
||||||
def test_loops(self) -> None:
|
def test_loops(self) -> None:
|
||||||
m = self.stom(3*".. ..\n")
|
m = self.stom(3 * ".. ..\n")
|
||||||
self.generator.add_loop(m.tiles, 1, 3)
|
self.generator.add_loop(m.tiles, 1, 3)
|
||||||
s = self.mtos(m)
|
s = self.mtos(m)
|
||||||
self.assertEqual(s, ".. ..\n.......\n.. ..")
|
self.assertEqual(s, ".. ..\n.......\n.. ..")
|
||||||
self.assertFalse(self.generator.add_loop(m.tiles, 0, 0))
|
self.assertFalse(self.generator.add_loop(m.tiles, 0, 0))
|
||||||
m = self.stom("...\n. .\n...")
|
m = self.stom("...\n. .\n...")
|
||||||
self.assertFalse(self.generator.add_loop(m.tiles, 1, 1))
|
self.assertFalse(self.generator.add_loop(m.tiles, 1, 1))
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
|
@ -55,6 +55,10 @@ class TestTranslations(unittest.TestCase):
|
||||||
self.assertEqual(_("Key used to wait"), "Touche pour attendre")
|
self.assertEqual(_("Key used to wait"), "Touche pour attendre")
|
||||||
self.assertEqual(_("Key used to use ladders"),
|
self.assertEqual(_("Key used to use ladders"),
|
||||||
"Touche pour utiliser les échelles")
|
"Touche pour utiliser les échelles")
|
||||||
|
self.assertEqual(_("Key used to use a bow"),
|
||||||
|
"Touche pour utiliser un arc")
|
||||||
|
self.assertEqual(_("Key used to dance"),
|
||||||
|
"Touche pour danser")
|
||||||
self.assertEqual(_("Texture pack"), "Pack de textures")
|
self.assertEqual(_("Texture pack"), "Pack de textures")
|
||||||
self.assertEqual(_("Language"), "Langue")
|
self.assertEqual(_("Language"), "Langue")
|
||||||
|
|
||||||
|
@ -77,6 +81,11 @@ class TestTranslations(unittest.TestCase):
|
||||||
self.assertEqual(_("helmet"), "casque")
|
self.assertEqual(_("helmet"), "casque")
|
||||||
self.assertEqual(_("chestplate"), "plastron")
|
self.assertEqual(_("chestplate"), "plastron")
|
||||||
self.assertEqual(_("shield"), "bouclier")
|
self.assertEqual(_("shield"), "bouclier")
|
||||||
|
self.assertEqual(_("ruler"), "règle")
|
||||||
|
self.assertEqual(_("scroll of damage"), "parchemin de dégâts")
|
||||||
|
self.assertEqual(_("scroll of weakness"), "parchemin de faiblesse")
|
||||||
|
self.assertEqual(_("bow"), "arc")
|
||||||
|
self.assertEqual(_("fire ball staff"), "baton de boule de feu")
|
||||||
self.assertEqual(_("ring of critical damage"),
|
self.assertEqual(_("ring of critical damage"),
|
||||||
"anneau de coup critique")
|
"anneau de coup critique")
|
||||||
self.assertEqual(_("ring of more experience"),
|
self.assertEqual(_("ring of more experience"),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
|
# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import gettext as gt
|
import gettext as gt
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue