Compare commits
33 Commits
map_genera
...
master
Author | SHA1 | Date |
---|---|---|
ynerant | 4c274bebb9 | |
ynerant | a28909bb70 | |
ynerant | 6566f5140a | |
Yohann D'ANELLO | 776f8ed88c | |
ynerant | 284a22c92e | |
Yohann D'ANELLO | 3d019d3ca8 | |
ynerant | 99b749aaa2 | |
Yohann D'ANELLO | d978d319bc | |
Yohann D'ANELLO | 87e896bd06 | |
ynerant | e233243b81 | |
Yohann D'ANELLO | 7ce3b8cd5d | |
ynerant | fa0a0a79ea | |
Yohann D'ANELLO | d839356b2a | |
Yohann D'ANELLO | 7b019ce149 | |
Charles Peyrat | 03c45a970c | |
Charles Peyrat | 79d8ef3a44 | |
ynerant | 6294f9c07f | |
Yohann D'ANELLO | 57605c969f | |
ynerant | e9374c5e6b | |
ynerant | b72e41d14d | |
Yohann D'ANELLO | 588357e5bf | |
ynerant | 2031d7fa67 | |
Yohann D'ANELLO | 65ae99a26d | |
Yohann D'ANELLO | 60675d7859 | |
Yohann D'ANELLO | b0ca1d4edf | |
Yohann D'ANELLO | 8f845d1e4c | |
Yohann D'ANELLO | 11daa8573c | |
Yohann D'ANELLO | 6c0aaffd77 | |
Yohann D'ANELLO | e744310861 | |
nicomarg | 96e9612d16 | |
Nicolas Margulies | f05652d9b8 | |
Nicolas Margulies | 519504fc32 | |
Nicolas Margulies | 88471f4361 |
32
README.md
32
README.md
|
@ -11,31 +11,37 @@
|
||||||
|
|
||||||
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.
|
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
|
## Installation
|
||||||
|
|
||||||
####Via PyPI :
|
#### Via PyPI :
|
||||||
``` pip install --user squirrel-battle
|
```
|
||||||
``` to install
|
$ pip install --user squirrel-battle
|
||||||
|
```
|
||||||
|
to install
|
||||||
|
|
||||||
``` pip install --user --upgrade squirrel-battle
|
```
|
||||||
``` to upgrade
|
$ pip install --user --upgrade squirrel-battle
|
||||||
|
```
|
||||||
|
to upgrade
|
||||||
|
|
||||||
####Via ArchLinux package :
|
#### Via ArchLinux package :
|
||||||
Download one of these two packages on the AUR :
|
Download one of these two packages on the AUR :
|
||||||
|
|
||||||
* python-squirrel-battle
|
* python-squirrel-battle
|
||||||
* python-squirrel-battle-git
|
* python-squirrel-battle-git
|
||||||
|
|
||||||
####Via Debian package :
|
#### Via Debian package :
|
||||||
Available on our git repository, has a dependency on fonts-noto-color-emoji (to be found in the official Debian repositories).
|
Available on our git repository, has a dependency on fonts-noto-color-emoji (to be found in the official Debian repositories).
|
||||||
|
|
||||||
Run ```
|
Run
|
||||||
dpkg -i python3-squirrelbattle_3.14.1_all.deb
|
```
|
||||||
``` after downloading
|
$ dpkg -i python3-squirrelbattle_23.14_all.deb
|
||||||
|
```
|
||||||
|
after downloading
|
||||||
|
|
||||||
In all cases, execute via command line : ```squirrel-battle```
|
In all cases, execute via command line : `squirrel-battle`
|
||||||
|
|
||||||
##For first-time players
|
## 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.
|
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.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
python3-squirrel-battle (23.14) beta; urgency=low
|
||||||
|
|
||||||
|
* Big update
|
||||||
|
|
||||||
|
-- Yohann D'ANELLO <squirrel-battle@crans.org> Sun, 10 Jan 2021 23:56:42 +0100
|
||||||
|
|
||||||
python3-squirrel-battle (3.14.1) beta; urgency=low
|
python3-squirrel-battle (3.14.1) beta; urgency=low
|
||||||
|
|
||||||
* Some graphical improvements.
|
* Some graphical improvements.
|
||||||
|
|
|
@ -3,7 +3,7 @@ Déploiement du projet
|
||||||
|
|
||||||
.. _PyPI: https://pypi.org/project/squirrel-battle/
|
.. _PyPI: https://pypi.org/project/squirrel-battle/
|
||||||
.. _AUR: https://aur.archlinux.org/packages/python-squirrel-battle/
|
.. _AUR: https://aur.archlinux.org/packages/python-squirrel-battle/
|
||||||
.. _Debian: https://gitlab.crans.org/ynerant/squirrel-battle/-/jobs/artifacts/master/raw/build/python3-squirrelbattle_3.14.1_all.deb?job=build-deb
|
.. _Debian: https://gitlab.crans.org/ynerant/squirrel-battle/-/jobs/artifacts/master/raw/build/python3-squirrelbattle_23.14_all.deb?job=build-deb
|
||||||
.. _installation: install.html
|
.. _installation: install.html
|
||||||
|
|
||||||
À chaque nouvelle version du projet, il est compilé et déployé dans PyPI_, dans
|
À chaque nouvelle version du projet, il est compilé et déployé dans PyPI_, dans
|
||||||
|
@ -46,7 +46,7 @@ paquet ainsi que des détails à fournir à PyPI :
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="squirrel-battle",
|
name="squirrel-battle",
|
||||||
version="3.14.1",
|
version="23.14",
|
||||||
author="ÿnérant, eichhornchen, nicomarg, charlse",
|
author="ÿnérant, eichhornchen, nicomarg, charlse",
|
||||||
author_email="squirrel-battle@crans.org",
|
author_email="squirrel-battle@crans.org",
|
||||||
description="Watch out for squirrel's knives!",
|
description="Watch out for squirrel's knives!",
|
||||||
|
@ -172,7 +172,7 @@ du dépôt Git. Le fichier ``PKGBUILD`` dispose de cette structure :
|
||||||
|
|
||||||
pkgbase=squirrel-battle
|
pkgbase=squirrel-battle
|
||||||
pkgname=python-squirrel-battle-git
|
pkgname=python-squirrel-battle-git
|
||||||
pkgver=3.14.1
|
pkgver=23.14
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Watch out for squirrel's knives!"
|
pkgdesc="Watch out for squirrel's knives!"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
|
@ -222,7 +222,7 @@ les releases, est plus ou moins similaire :
|
||||||
|
|
||||||
pkgbase=squirrel-battle
|
pkgbase=squirrel-battle
|
||||||
pkgname=python-squirrel-battle
|
pkgname=python-squirrel-battle
|
||||||
pkgver=3.14.1
|
pkgver=23.14
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Watch out for squirrel's knives!"
|
pkgdesc="Watch out for squirrel's knives!"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
|
@ -232,7 +232,7 @@ les releases, est plus ou moins similaire :
|
||||||
makedepends=('gettext' 'python-setuptools')
|
makedepends=('gettext' 'python-setuptools')
|
||||||
depends=('noto-fonts-emoji')
|
depends=('noto-fonts-emoji')
|
||||||
checkdepends=('python-tox')
|
checkdepends=('python-tox')
|
||||||
source=("https://gitlab.crans.org/ynerant/squirrel-battle/-/archive/v3.14.1/$pkgbase-v$pkgver.tar.gz")
|
source=("https://gitlab.crans.org/ynerant/squirrel-battle/-/archive/v23.14/$pkgbase-v$pkgver.tar.gz")
|
||||||
sha256sums=("6090534d598c0b3a8f5acdb553c12908ba8107d62d08e17747d1dbb397bddef0")
|
sha256sums=("6090534d598c0b3a8f5acdb553c12908ba8107d62d08e17747d1dbb397bddef0")
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
|
@ -317,7 +317,7 @@ On peut ensuite construire le paquet :
|
||||||
dpkg-buildpackage
|
dpkg-buildpackage
|
||||||
mkdir build && cp ../*.deb build/
|
mkdir build && cp ../*.deb build/
|
||||||
|
|
||||||
Le paquet sera installé dans ``build/python3-squirrel-battle_3.14.1_all.deb``.
|
Le paquet sera installé dans ``build/python3-squirrel-battle_23.14_all.deb``.
|
||||||
|
|
||||||
Le paquet Debian_ est construit par l'intégration continue Gitlab et ajouté
|
Le paquet Debian_ est construit par l'intégration continue Gitlab et ajouté
|
||||||
à chaque release.
|
à chaque release.
|
||||||
|
|
|
@ -61,7 +61,7 @@ Le jeu peut être ensuite lancé via la commande ``squirrel-battle``.
|
||||||
Sur Ubuntu/Debian
|
Sur Ubuntu/Debian
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. _paquet: https://gitlab.crans.org/ynerant/squirrel-battle/-/jobs/artifacts/master/raw/build/python3-squirrelbattle_3.14.1_all.deb?job=build-deb
|
.. _paquet: https://gitlab.crans.org/ynerant/squirrel-battle/-/jobs/artifacts/master/raw/build/python3-squirrelbattle_23.14_all.deb?job=build-deb
|
||||||
|
|
||||||
Un paquet_ est généré par l'intégration continue de Gitlab à chaque commit.
|
Un paquet_ est généré par l'intégration continue de Gitlab à chaque commit.
|
||||||
Ils sont également attachés à chaque nouvelle release.
|
Ils sont également attachés à chaque nouvelle release.
|
||||||
|
@ -73,7 +73,7 @@ Pour installer ce paquet, il suffit de le télécharger et d'appeler ``dpkg`` :
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
dpkg -i python3-squirrelbattle_3.14.1_all.deb
|
dpkg -i python3-squirrelbattle_23.14_all.deb
|
||||||
|
|
||||||
Ce paquet inclut un patch pour afficher les émojis écureuil correctement.
|
Ce paquet inclut un patch pour afficher les émojis écureuil correctement.
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ d'exécuter pour chaque langue ``<LANG>`` :
|
||||||
find squirrelbattle -iname '*.py' | xargs xgettext --from-code utf-8
|
find squirrelbattle -iname '*.py' | xargs xgettext --from-code utf-8
|
||||||
--add-comments
|
--add-comments
|
||||||
--package-name=squirrelbattle
|
--package-name=squirrelbattle
|
||||||
--package-version=3.14.1
|
--package-version=23.14
|
||||||
"--copyright-holder=ÿnérant, eichhornchen, nicomarg, charlse"
|
"--copyright-holder=ÿnérant, eichhornchen, nicomarg, charlse"
|
||||||
--msgid-bugs-address=squirrel-battle@crans.org
|
--msgid-bugs-address=squirrel-battle@crans.org
|
||||||
-o squirrelbattle/locale/<LANG>/LC_MESSAGES/squirrelbattle.po
|
-o squirrelbattle/locale/<LANG>/LC_MESSAGES/squirrelbattle.po
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -22,7 +22,7 @@ for language in ["de", "es", "fr"]:
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="squirrel-battle",
|
name="squirrel-battle",
|
||||||
version="3.14.1",
|
version="23.14",
|
||||||
author="ÿnérant, eichhornchen, nicomarg, charlse",
|
author="ÿnérant, eichhornchen, nicomarg, charlse",
|
||||||
author_email="squirrel-battle@crans.org",
|
author_email="squirrel-battle@crans.org",
|
||||||
description="Watch out for squirrel's knives!",
|
description="Watch out for squirrel's knives!",
|
||||||
|
|
|
@ -2,17 +2,17 @@
|
||||||
####### #############
|
####### #############
|
||||||
#.H...# #...........#
|
#.H...# #...........#
|
||||||
#.....# #####...........#
|
#.....# #####...........#
|
||||||
#.....# #............H..#
|
#.....# #...&........H..#
|
||||||
#.##### #.###...........#
|
#.##### #.###...........#
|
||||||
#.# #.# #...........#
|
#.# #.# #...........#
|
||||||
#.# #.# #############
|
#.# #.# #############
|
||||||
#.# #.#
|
#.# #.#
|
||||||
#.#### #.#
|
#.#### #.#
|
||||||
#....# #.#
|
#....# #.#
|
||||||
####.###################.#
|
####&###################&#
|
||||||
#.....................# #################
|
#.....................# #################
|
||||||
#.....................# #...............#
|
#.....................# #...............#
|
||||||
#.....................#######...............#
|
#.....................#######...............#
|
||||||
#...........................................#
|
#.....................&.....&...............#
|
||||||
#.....................#######...............#
|
#.....................#######...............#
|
||||||
####################### #################
|
####################### #################
|
||||||
|
|
|
@ -176,7 +176,8 @@ class PlayerInventoryDisplay(MenuDisplay):
|
||||||
selection = f"[{rep}]" if i == self.menu.position \
|
selection = f"[{rep}]" if i == self.menu.position \
|
||||||
and self.selected else f" {rep} "
|
and self.selected else f" {rep} "
|
||||||
self.addstr(self.pad, i + 1, 0, selection
|
self.addstr(self.pad, i + 1, 0, selection
|
||||||
+ " " + item.translated_name.capitalize()
|
+ " " + ("[E]" if item.equipped else "")
|
||||||
|
+ item.translated_name.capitalize()
|
||||||
+ (f" ({item.description})" if item.description else "")
|
+ (f" ({item.description})" if item.description else "")
|
||||||
+ (": " + str(item.price) + " Hazels"
|
+ (": " + str(item.price) + " Hazels"
|
||||||
if self.store_mode else ""))
|
if self.store_mode else ""))
|
||||||
|
|
|
@ -82,6 +82,7 @@ TexturePack.ASCII_PACK = TexturePack(
|
||||||
BOW=')',
|
BOW=')',
|
||||||
CHEST='□',
|
CHEST='□',
|
||||||
CHESTPLATE='(',
|
CHESTPLATE='(',
|
||||||
|
DOOR='&',
|
||||||
EAGLE='µ',
|
EAGLE='µ',
|
||||||
EMPTY=' ',
|
EMPTY=' ',
|
||||||
EXPLOSION='%',
|
EXPLOSION='%',
|
||||||
|
@ -124,6 +125,8 @@ TexturePack.SQUIRREL_PACK = TexturePack(
|
||||||
BOW='🏹',
|
BOW='🏹',
|
||||||
CHEST='🧰',
|
CHEST='🧰',
|
||||||
CHESTPLATE='🦺',
|
CHESTPLATE='🦺',
|
||||||
|
DOOR=('🚪', curses.COLOR_WHITE, (1000, 1000, 1000),
|
||||||
|
curses.COLOR_WHITE, (1000, 1000, 1000)),
|
||||||
EAGLE='🦅',
|
EAGLE='🦅',
|
||||||
EMPTY=' ',
|
EMPTY=' ',
|
||||||
EXPLOSION='💥',
|
EXPLOSION='💥',
|
||||||
|
|
|
@ -12,16 +12,19 @@ class Item(Entity):
|
||||||
"""
|
"""
|
||||||
A class for items.
|
A class for items.
|
||||||
"""
|
"""
|
||||||
held: bool
|
|
||||||
held_by: Optional[InventoryHolder]
|
held_by: Optional[InventoryHolder]
|
||||||
price: int
|
price: int
|
||||||
|
|
||||||
def __init__(self, held: bool = False,
|
def __init__(self, equipped: bool = False,
|
||||||
held_by: Optional[InventoryHolder] = None,
|
held_by: Optional[InventoryHolder] = None,
|
||||||
|
hold_slot: str = "equipped_secondary",
|
||||||
price: int = 2, *args, **kwargs):
|
price: int = 2, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.held = held
|
|
||||||
self.held_by = held_by
|
self.held_by = held_by
|
||||||
|
self.equipped = equipped
|
||||||
|
self.hold_slot = hold_slot
|
||||||
|
if equipped:
|
||||||
|
self.equip()
|
||||||
self.price = price
|
self.price = price
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -35,11 +38,11 @@ class Item(Entity):
|
||||||
"""
|
"""
|
||||||
The item is dropped from the inventory onto the floor.
|
The item is dropped from the inventory onto the floor.
|
||||||
"""
|
"""
|
||||||
if self.held:
|
if self.held_by is not None:
|
||||||
|
self.unequip()
|
||||||
self.held_by.remove_from_inventory(self)
|
self.held_by.remove_from_inventory(self)
|
||||||
self.held_by.map.add_entity(self)
|
self.held_by.map.add_entity(self)
|
||||||
self.move(self.held_by.y, self.held_by.x)
|
self.move(self.held_by.y, self.held_by.x)
|
||||||
self.held = False
|
|
||||||
self.held_by = None
|
self.held_by = None
|
||||||
|
|
||||||
def use(self) -> None:
|
def use(self) -> None:
|
||||||
|
@ -52,28 +55,41 @@ class Item(Entity):
|
||||||
Indicates what should be done when the item is thrown.
|
Indicates what should be done when the item is thrown.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def on_equip(self) -> None:
|
||||||
|
"""
|
||||||
|
Indicates a special behaviour when equipping
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_unequip(self) -> None:
|
||||||
|
"""
|
||||||
|
Indicates a special behaviour when unequipping
|
||||||
|
"""
|
||||||
|
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
# Other objects are only equipped as secondary.
|
# Other objects are only equipped as secondary.
|
||||||
if self.held_by.equipped_secondary:
|
if not self.equipped:
|
||||||
self.held_by.equipped_secondary.unequip()
|
if getattr(self.held_by, self.hold_slot):
|
||||||
self.held_by.remove_from_inventory(self)
|
getattr(self.held_by, self.hold_slot).unequip()
|
||||||
self.held_by.equipped_secondary = self
|
self.equipped = True
|
||||||
|
setattr(self.held_by, self.hold_slot, self)
|
||||||
|
self.on_equip()
|
||||||
|
|
||||||
def unequip(self) -> None:
|
def unequip(self) -> None:
|
||||||
"""
|
"""
|
||||||
Indicates what should be done when the item is unequipped.
|
Indicates what should be done when the item is unequipped.
|
||||||
"""
|
"""
|
||||||
self.held_by.remove_from_inventory(self)
|
if self.equipped:
|
||||||
self.held_by.add_to_inventory(self)
|
setattr(self.held_by, self.hold_slot, None)
|
||||||
|
self.equipped = False
|
||||||
|
self.on_unequip()
|
||||||
|
|
||||||
def hold(self, holder: InventoryHolder) -> None:
|
def hold(self, holder: InventoryHolder) -> None:
|
||||||
"""
|
"""
|
||||||
The item is taken from the floor and put into the inventory.
|
The item is taken from the floor and put into the inventory.
|
||||||
"""
|
"""
|
||||||
self.held = True
|
|
||||||
self.held_by = holder
|
self.held_by = holder
|
||||||
self.held_by.map.remove_entity(self)
|
self.held_by.map.remove_entity(self)
|
||||||
holder.add_to_inventory(self)
|
holder.add_to_inventory(self)
|
||||||
|
@ -83,7 +99,7 @@ class Item(Entity):
|
||||||
Saves the state of the item into a dictionary.
|
Saves the state of the item into a dictionary.
|
||||||
"""
|
"""
|
||||||
d = super().save_state()
|
d = super().save_state()
|
||||||
d["held"] = self.held
|
d["equipped"] = self.equipped
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -103,10 +119,12 @@ class Item(Entity):
|
||||||
inventory.
|
inventory.
|
||||||
"""
|
"""
|
||||||
if for_free:
|
if for_free:
|
||||||
|
self.unequip() if self.equipped else None
|
||||||
self.hold(buyer)
|
self.hold(buyer)
|
||||||
seller.remove_from_inventory(self)
|
seller.remove_from_inventory(self)
|
||||||
return True
|
return True
|
||||||
elif buyer.hazel >= self.price:
|
elif buyer.hazel >= self.price:
|
||||||
|
self.unequip() if self.equipped else None
|
||||||
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)
|
||||||
|
@ -169,7 +187,7 @@ class Bomb(Item):
|
||||||
"""
|
"""
|
||||||
When the bomb is used, it is thrown and then it explodes.
|
When the bomb is used, it is thrown and then it explodes.
|
||||||
"""
|
"""
|
||||||
if self.held:
|
if self.held_by is not None:
|
||||||
self.owner = self.held_by
|
self.owner = self.held_by
|
||||||
super().drop()
|
super().drop()
|
||||||
self.exploding = True
|
self.exploding = True
|
||||||
|
@ -214,14 +232,19 @@ class Explosion(Item):
|
||||||
"""
|
"""
|
||||||
When a bomb explodes, the explosion is displayed.
|
When a bomb explodes, the explosion is displayed.
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
living_ticks: int
|
||||||
|
|
||||||
|
def __init__(self, living_ticks: int = 2, *args, **kwargs):
|
||||||
super().__init__(name="explosion", *args, **kwargs)
|
super().__init__(name="explosion", *args, **kwargs)
|
||||||
|
self.living_ticks = living_ticks
|
||||||
|
|
||||||
def act(self, m: Map) -> None:
|
def act(self, m: Map) -> None:
|
||||||
"""
|
"""
|
||||||
The bomb disappears after exploding.
|
The bomb disappears after exploding.
|
||||||
"""
|
"""
|
||||||
m.remove_entity(self)
|
self.living_ticks -= 1
|
||||||
|
if self.living_ticks <= 0:
|
||||||
|
m.remove_entity(self)
|
||||||
|
|
||||||
def hold(self, player: InventoryHolder) -> None:
|
def hold(self, player: InventoryHolder) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -236,7 +259,7 @@ class Weapon(Item):
|
||||||
damage: int
|
damage: int
|
||||||
|
|
||||||
def __init__(self, damage: int = 3, *args, **kwargs):
|
def __init__(self, damage: int = 3, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(hold_slot="equipped_main", *args, **kwargs)
|
||||||
self.damage = damage
|
self.damage = damage
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -251,20 +274,17 @@ class Weapon(Item):
|
||||||
d["damage"] = self.damage
|
d["damage"] = self.damage
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def equip(self) -> None:
|
def on_equip(self) -> None:
|
||||||
"""
|
"""
|
||||||
When a weapon is equipped, the player gains strength.
|
When a weapon is equipped, the player gains strength.
|
||||||
"""
|
"""
|
||||||
self.held_by.remove_from_inventory(self)
|
|
||||||
self.held_by.equipped_main = self
|
|
||||||
self.held_by.strength += self.damage
|
self.held_by.strength += self.damage
|
||||||
|
|
||||||
def unequip(self) -> None:
|
def on_unequip(self) -> None:
|
||||||
"""
|
"""
|
||||||
Remove the strength earned by the weapon.
|
Remove the strength earned by the weapon.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
super().unequip()
|
|
||||||
self.held_by.strength -= self.damage
|
self.held_by.strength -= self.damage
|
||||||
|
|
||||||
|
|
||||||
|
@ -301,12 +321,10 @@ class Armor(Item):
|
||||||
return f"CON+{self.constitution}" if self.constitution \
|
return f"CON+{self.constitution}" if self.constitution \
|
||||||
else super().description
|
else super().description
|
||||||
|
|
||||||
def equip(self) -> None:
|
def on_equip(self) -> None:
|
||||||
super().equip()
|
|
||||||
self.held_by.constitution += self.constitution
|
self.held_by.constitution += self.constitution
|
||||||
|
|
||||||
def unequip(self) -> None:
|
def on_unequip(self) -> None:
|
||||||
super().unequip()
|
|
||||||
self.held_by.constitution -= self.constitution
|
self.held_by.constitution -= self.constitution
|
||||||
|
|
||||||
def save_state(self) -> dict:
|
def save_state(self) -> dict:
|
||||||
|
@ -332,13 +350,7 @@ class Helmet(Armor):
|
||||||
def __init__(self, name: str = "helmet", constitution: int = 2,
|
def __init__(self, name: str = "helmet", constitution: int = 2,
|
||||||
price: int = 18, *args, **kwargs):
|
price: int = 18, *args, **kwargs):
|
||||||
super().__init__(name=name, constitution=constitution, price=price,
|
super().__init__(name=name, constitution=constitution, price=price,
|
||||||
*args, **kwargs)
|
hold_slot="equipped_helmet", *args, **kwargs)
|
||||||
|
|
||||||
def equip(self) -> None:
|
|
||||||
if self.held_by.equipped_helmet:
|
|
||||||
self.held_by.equipped_helmet.unequip()
|
|
||||||
self.held_by.remove_from_inventory(self)
|
|
||||||
self.held_by.equipped_helmet = self
|
|
||||||
|
|
||||||
|
|
||||||
class Chestplate(Armor):
|
class Chestplate(Armor):
|
||||||
|
@ -348,13 +360,7 @@ class Chestplate(Armor):
|
||||||
def __init__(self, name: str = "chestplate", constitution: int = 4,
|
def __init__(self, name: str = "chestplate", constitution: int = 4,
|
||||||
price: int = 30, *args, **kwargs):
|
price: int = 30, *args, **kwargs):
|
||||||
super().__init__(name=name, constitution=constitution, price=price,
|
super().__init__(name=name, constitution=constitution, price=price,
|
||||||
*args, **kwargs)
|
hold_slot="equipped_armor", *args, **kwargs)
|
||||||
|
|
||||||
def equip(self) -> None:
|
|
||||||
if self.held_by.equipped_armor:
|
|
||||||
self.held_by.equipped_armor.unequip()
|
|
||||||
self.held_by.remove_from_inventory(self)
|
|
||||||
self.held_by.equipped_armor = self
|
|
||||||
|
|
||||||
|
|
||||||
class BodySnatchPotion(Item):
|
class BodySnatchPotion(Item):
|
||||||
|
@ -426,8 +432,7 @@ class Ring(Item):
|
||||||
("CRI", self.critical), ("XP", self.experience)]
|
("CRI", self.critical), ("XP", self.experience)]
|
||||||
return ", ".join(f"{key}+{value}" for key, value in fields if value)
|
return ", ".join(f"{key}+{value}" for key, value in fields if value)
|
||||||
|
|
||||||
def equip(self) -> None:
|
def on_equip(self) -> None:
|
||||||
super().equip()
|
|
||||||
self.held_by.maxhealth += self.maxhealth
|
self.held_by.maxhealth += self.maxhealth
|
||||||
self.held_by.strength += self.strength
|
self.held_by.strength += self.strength
|
||||||
self.held_by.intelligence += self.intelligence
|
self.held_by.intelligence += self.intelligence
|
||||||
|
@ -437,8 +442,7 @@ class Ring(Item):
|
||||||
self.held_by.critical += self.critical
|
self.held_by.critical += self.critical
|
||||||
self.held_by.xp_buff += self.experience
|
self.held_by.xp_buff += self.experience
|
||||||
|
|
||||||
def unequip(self) -> None:
|
def on_unequip(self) -> None:
|
||||||
super().unequip()
|
|
||||||
self.held_by.maxhealth -= self.maxhealth
|
self.held_by.maxhealth -= self.maxhealth
|
||||||
self.held_by.strength -= self.strength
|
self.held_by.strength -= self.strength
|
||||||
self.held_by.intelligence -= self.intelligence
|
self.held_by.intelligence -= self.intelligence
|
||||||
|
@ -557,13 +561,6 @@ class LongRangeWeapon(Weapon):
|
||||||
self.held_by.map.logs.add_message(line)
|
self.held_by.map.logs.add_message(line)
|
||||||
return (to_kill.y, to_kill.x) if to_kill else None
|
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
|
@property
|
||||||
def stat(self) -> str:
|
def stat(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,7 +6,7 @@ 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, Tile
|
||||||
from ..translations import gettext as _
|
from ..translations import gettext as _
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,21 +117,6 @@ class Player(InventoryHolder, FightingEntity):
|
||||||
self.current_xp += int(xp * self.xp_buff)
|
self.current_xp += int(xp * self.xp_buff)
|
||||||
self.level_up()
|
self.level_up()
|
||||||
|
|
||||||
def remove_from_inventory(self, obj: Item) -> None:
|
|
||||||
"""
|
|
||||||
Remove the given item from the inventory, even if the item is equipped.
|
|
||||||
"""
|
|
||||||
if obj == self.equipped_main:
|
|
||||||
self.equipped_main = None
|
|
||||||
elif obj == self.equipped_armor:
|
|
||||||
self.equipped_armor = None
|
|
||||||
elif obj == self.equipped_secondary:
|
|
||||||
self.equipped_secondary = None
|
|
||||||
elif obj == self.equipped_helmet:
|
|
||||||
self.equipped_helmet = None
|
|
||||||
else:
|
|
||||||
return super().remove_from_inventory(obj)
|
|
||||||
|
|
||||||
# noinspection PyTypeChecker,PyUnresolvedReferences
|
# noinspection PyTypeChecker,PyUnresolvedReferences
|
||||||
def check_move(self, y: int, x: int, move_if_possible: bool = False) \
|
def check_move(self, y: int, x: int, move_if_possible: bool = False) \
|
||||||
-> bool:
|
-> bool:
|
||||||
|
@ -152,6 +137,12 @@ class Player(InventoryHolder, FightingEntity):
|
||||||
return True
|
return True
|
||||||
elif entity.is_item():
|
elif entity.is_item():
|
||||||
entity.hold(self)
|
entity.hold(self)
|
||||||
|
tile = self.map.tiles[y][x]
|
||||||
|
if tile == Tile.DOOR and move_if_possible:
|
||||||
|
# Open door
|
||||||
|
self.map.tiles[y][x] = Tile.FLOOR
|
||||||
|
self.map.compute_visibility(y, x, self.vision)
|
||||||
|
return super().check_move(y, x, move_if_possible)
|
||||||
return super().check_move(y, x, move_if_possible)
|
return super().check_move(y, x, move_if_possible)
|
||||||
|
|
||||||
def save_state(self) -> dict:
|
def save_state(self) -> dict:
|
||||||
|
|
|
@ -192,14 +192,16 @@ class Game:
|
||||||
|
|
||||||
# We move up on the ladder of the beginning,
|
# We move up on the ladder of the beginning,
|
||||||
# down at the end of the stage
|
# down at the end of the stage
|
||||||
move_down = y != self.map.start_y and x != self.map.start_x
|
move_down = y != self.map.start_y or x != self.map.start_x
|
||||||
old_map = self.map
|
old_map = self.map
|
||||||
self.map_index += 1 if move_down else -1
|
self.map_index += 1 if move_down else -1
|
||||||
if self.map_index == -1:
|
if self.map_index == -1:
|
||||||
self.map_index = 0
|
self.map_index = 0
|
||||||
return
|
return
|
||||||
while self.map_index >= len(self.maps):
|
while self.map_index >= len(self.maps):
|
||||||
self.maps.append(broguelike.Generator().run())
|
m = broguelike.Generator().run()
|
||||||
|
m.logs = self.logs
|
||||||
|
self.maps.append(m)
|
||||||
new_map = self.map
|
new_map = self.map
|
||||||
new_map.floor = self.map_index
|
new_map.floor = self.map_index
|
||||||
old_map.remove_entity(self.player)
|
old_map.remove_entity(self.player)
|
||||||
|
@ -305,7 +307,8 @@ class Game:
|
||||||
if key == KeyValues.USE:
|
if key == KeyValues.USE:
|
||||||
self.inventory_menu.validate().use()
|
self.inventory_menu.validate().use()
|
||||||
elif key == KeyValues.EQUIP:
|
elif key == KeyValues.EQUIP:
|
||||||
self.inventory_menu.validate().equip()
|
item = self.inventory_menu.validate()
|
||||||
|
item.unequip() if item.equipped else item.equip()
|
||||||
elif key == KeyValues.DROP:
|
elif key == KeyValues.DROP:
|
||||||
self.inventory_menu.validate().drop()
|
self.inventory_menu.validate().drop()
|
||||||
|
|
||||||
|
@ -417,6 +420,7 @@ 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
|
||||||
|
m.logs = self.logs
|
||||||
except KeyError as error:
|
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.")\
|
||||||
|
|
|
@ -390,6 +390,7 @@ class Tile(Enum):
|
||||||
WALL = auto()
|
WALL = auto()
|
||||||
FLOOR = auto()
|
FLOOR = auto()
|
||||||
LADDER = auto()
|
LADDER = auto()
|
||||||
|
DOOR = auto()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_ascii_char(ch: str) -> "Tile":
|
def from_ascii_char(ch: str) -> "Tile":
|
||||||
|
@ -430,7 +431,7 @@ class Tile(Enum):
|
||||||
"""
|
"""
|
||||||
Is this Tile a wall?
|
Is this Tile a wall?
|
||||||
"""
|
"""
|
||||||
return self == Tile.WALL
|
return self == Tile.WALL or self == Tile.DOOR
|
||||||
|
|
||||||
def is_ladder(self) -> bool:
|
def is_ladder(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -857,7 +858,9 @@ class InventoryHolder(Entity):
|
||||||
entity_classes = self.get_all_entity_classes_in_a_dict()
|
entity_classes = self.get_all_entity_classes_in_a_dict()
|
||||||
|
|
||||||
item_class = entity_classes[item_dict["type"]]
|
item_class = entity_classes[item_dict["type"]]
|
||||||
return item_class(**item_dict)
|
item = item_class(**item_dict)
|
||||||
|
item.held_by = self
|
||||||
|
return item
|
||||||
|
|
||||||
def save_state(self) -> dict:
|
def save_state(self) -> dict:
|
||||||
"""
|
"""
|
||||||
|
@ -873,6 +876,7 @@ class InventoryHolder(Entity):
|
||||||
Adds an object to the inventory.
|
Adds an object to the inventory.
|
||||||
"""
|
"""
|
||||||
if obj not in self.inventory:
|
if obj not in self.inventory:
|
||||||
|
obj.held_by = self
|
||||||
self.inventory.append(obj)
|
self.inventory.append(obj)
|
||||||
|
|
||||||
def remove_from_inventory(self, obj: Any) -> None:
|
def remove_from_inventory(self, obj: Any) -> None:
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
"Project-Id-Version: squirrelbattle 23.14\n"
|
||||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
||||||
"POT-Creation-Date: 2021-01-10 21:30+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"
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
"Project-Id-Version: squirrelbattle 23.14\n"
|
||||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
||||||
"POT-Creation-Date: 2021-01-10 21:30+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"
|
||||||
|
@ -23,19 +23,19 @@ msgstr "Inventorio :"
|
||||||
|
|
||||||
#: squirrelbattle/display/gamedisplay.py:167
|
#: squirrelbattle/display/gamedisplay.py:167
|
||||||
msgid "Equipped main:"
|
msgid "Equipped main:"
|
||||||
msgstr "Principal equipado:"
|
msgstr "Equipado principal :"
|
||||||
|
|
||||||
#: squirrelbattle/display/gamedisplay.py:171
|
#: squirrelbattle/display/gamedisplay.py:171
|
||||||
msgid "Equipped secondary:"
|
msgid "Equipped secondary:"
|
||||||
msgstr "Equipado secundario:"
|
msgstr "Equipado segundario :"
|
||||||
|
|
||||||
#: squirrelbattle/display/gamedisplay.py:176
|
#: squirrelbattle/display/gamedisplay.py:176
|
||||||
msgid "Equipped chestplate:"
|
msgid "Equipped chestplate:"
|
||||||
msgstr "Pechera equipada:"
|
msgstr "Pechera equipada :"
|
||||||
|
|
||||||
#: squirrelbattle/display/gamedisplay.py:180
|
#: squirrelbattle/display/gamedisplay.py:180
|
||||||
msgid "Equipped helmet:"
|
msgid "Equipped helmet:"
|
||||||
msgstr "Casco equipado:"
|
msgstr "Casco equipado :"
|
||||||
|
|
||||||
#: squirrelbattle/display/gamedisplay.py:187
|
#: squirrelbattle/display/gamedisplay.py:187
|
||||||
msgid "YOU ARE DEAD"
|
msgid "YOU ARE DEAD"
|
||||||
|
@ -44,22 +44,22 @@ msgstr "ERES MUERTO"
|
||||||
#: squirrelbattle/display/gamedisplay.py:191
|
#: 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 "Usa {key} para usar la escalera"
|
msgstr "Presiona {key} para utilizar la escala"
|
||||||
|
|
||||||
#: squirrelbattle/display/gamedisplay.py:210
|
#: 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 "Muévete hacia la entidad amiga para hablar con ella."
|
msgstr "Moverse hasta la entitad amistosa para hablar con ella"
|
||||||
|
|
||||||
#: squirrelbattle/display/gamedisplay.py:212
|
#: 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 "Usa {key} y luego muévete para hablar con la entidad"
|
msgstr "Presionar {key} pues moverse para hablar con la entitad"
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:124
|
#: squirrelbattle/display/menudisplay.py:124
|
||||||
#: squirrelbattle/display/menudisplay.py:149
|
#: squirrelbattle/display/menudisplay.py:149
|
||||||
#: squirrelbattle/display/menudisplay.py:304
|
#: squirrelbattle/display/menudisplay.py:304
|
||||||
msgid "Credits"
|
msgid "Credits"
|
||||||
msgstr "Creditos"
|
msgstr "Créditos"
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:173
|
#: squirrelbattle/display/menudisplay.py:173
|
||||||
msgid "INVENTORY"
|
msgid "INVENTORY"
|
||||||
|
@ -75,11 +75,11 @@ msgstr "COFRE"
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:308
|
#: squirrelbattle/display/menudisplay.py:308
|
||||||
msgid "Developers:"
|
msgid "Developers:"
|
||||||
msgstr "Desarrollador:"
|
msgstr "Desarrolladores :"
|
||||||
|
|
||||||
#: squirrelbattle/display/menudisplay.py:314
|
#: squirrelbattle/display/menudisplay.py:314
|
||||||
msgid "Translators:"
|
msgid "Translators:"
|
||||||
msgstr "Traductores:"
|
msgstr "Traductores :"
|
||||||
|
|
||||||
#: squirrelbattle/entities/friendly.py:38
|
#: squirrelbattle/entities/friendly.py:38
|
||||||
msgid "I don't sell any squirrel"
|
msgid "I don't sell any squirrel"
|
||||||
|
@ -153,12 +153,12 @@ msgstr "El baile no fue efectivo ..."
|
||||||
#: squirrelbattle/game.py:214
|
#: 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 "El jugador desciende alla planta {floor}."
|
msgstr "El jugador baja a la planta {floor}."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:227
|
#: 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 "El jugador sube por la planta {floor}."
|
msgstr "El jugador sube a la planta {floor}."
|
||||||
|
|
||||||
#: squirrelbattle/game.py:348 squirrelbattle/tests/game_test.py:631
|
#: 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"
|
||||||
|
@ -417,7 +417,7 @@ msgstr "anillo de daño crítico"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:91
|
#: squirrelbattle/tests/translations_test.py:91
|
||||||
msgid "ring of more experience"
|
msgid "ring of more experience"
|
||||||
msgstr "anillo de más experiencia"
|
msgstr "anillo de mejorada experiencia"
|
||||||
|
|
||||||
#: squirrelbattle/tests/translations_test.py:93
|
#: squirrelbattle/tests/translations_test.py:93
|
||||||
msgid "monocle"
|
msgid "monocle"
|
||||||
|
|
|
@ -10,7 +10,7 @@ msgstr "{name} prend {amount} points de dégât."
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: squirrelbattle 3.14.1\n"
|
"Project-Id-Version: squirrelbattle 23.14\n"
|
||||||
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
|
||||||
"POT-Creation-Date: 2021-01-10 21:30+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"
|
||||||
|
|
|
@ -21,38 +21,38 @@ DEFAULT_PARAMS = {
|
||||||
"large_circular_room": .10,
|
"large_circular_room": .10,
|
||||||
"circular_holes": .5,
|
"circular_holes": .5,
|
||||||
"loop_tries": 40,
|
"loop_tries": 40,
|
||||||
"loop_max": 8,
|
"loop_max": 5,
|
||||||
"loop_threshold": 15,
|
"loop_threshold": 15,
|
||||||
"spawn_per_region": [1, 2],
|
"spawn_per_region": [1, 2],
|
||||||
"room_chances" : {
|
"room_chances": {
|
||||||
"circular" : 1,
|
"circular": 5,
|
||||||
"chunks" : 1,
|
"chunks": 1,
|
||||||
"rectangle" : 1,
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_dist(level, y1, x1, y2, x2, threshold):
|
|
||||||
|
def dist(level: List[List[Tile]], y1: int, x1: int, y2: int, x2: int) -> int:
|
||||||
"""
|
"""
|
||||||
Returns whether the minimum walking distance between points (y1, x1) and
|
Compute the minimum walking distance between points (y1, x1) and (y2, x2)
|
||||||
(y2, x2) on the Tile grid level is greater than threshold
|
on a Tile grid
|
||||||
"""
|
"""
|
||||||
# simple breadth first search
|
# simple breadth first search
|
||||||
copy = [[t for t in row] for row 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 and dist < threshold:
|
while next_queue:
|
||||||
next_queue = []
|
next_queue = []
|
||||||
dist += 1
|
dist += 1
|
||||||
while queue:
|
while queue:
|
||||||
y, x = queue.pop()
|
y, x = queue.pop()
|
||||||
copy[y][x] = Tile.EMPTY
|
copy[y][x] = Tile.EMPTY
|
||||||
if y == y2 and x == x2:
|
if y == y2 and x == x2:
|
||||||
return False
|
return dist
|
||||||
for y, x in Map.neighbourhood(copy, y, x):
|
for y, x in Map.neighbourhood(copy, y, x):
|
||||||
if copy[y][x].can_walk():
|
if copy[y][x].can_walk():
|
||||||
next_queue.append([y, x])
|
next_queue.append([y, x])
|
||||||
queue = next_queue
|
queue = next_queue
|
||||||
return True
|
return -1
|
||||||
|
|
||||||
|
|
||||||
class Generator:
|
class Generator:
|
||||||
|
@ -103,8 +103,7 @@ class Generator:
|
||||||
making (door_y, door_x) in the room correspond with (y, x) in the level
|
making (door_y, door_x) in the room correspond with (y, x) in the level
|
||||||
"""
|
"""
|
||||||
rh, rw = len(room), len(room[0])
|
rh, rw = len(room), len(room[0])
|
||||||
# maybe place Tile.DOOR here instead ?
|
level[y][x] = Tile.DOOR
|
||||||
level[y][x] = Tile.FLOOR
|
|
||||||
for ry in range(rh):
|
for ry in range(rh):
|
||||||
for rx in range(rw):
|
for rx in range(rw):
|
||||||
if room[ry][rx] == Tile.FLOOR:
|
if room[ry][rx] == Tile.FLOOR:
|
||||||
|
@ -149,7 +148,7 @@ class Generator:
|
||||||
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 test_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
|
||||||
|
@ -218,8 +217,7 @@ class Generator:
|
||||||
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 not(0 <= y + i * dy < rh and 0 <= x + i * dx < rw) \
|
if room[y + i * dy][x + i * dx] != Tile.EMPTY:
|
||||||
or room[y + i * dy][x + i * dx] != Tile.EMPTY:
|
|
||||||
return False
|
return False
|
||||||
for i in range(length):
|
for i in range(length):
|
||||||
room[y + i * dy][x + i * dx] = Tile.FLOOR
|
room[y + i * dy][x + i * dx] = Tile.FLOOR
|
||||||
|
@ -255,33 +253,35 @@ class Generator:
|
||||||
if room[y][x] == Tile.EMPTY and \
|
if room[y][x] == Tile.EMPTY and \
|
||||||
Generator.build_door(room, y, x, dy, dx, length):
|
Generator.build_door(room, y, x, dy, dx, length):
|
||||||
break
|
break
|
||||||
|
else: # pragma: no cover
|
||||||
|
return None, None, None, None
|
||||||
|
|
||||||
return y + length * dy, x + length * dx, dy, dx
|
return y + length * dy, x + length * dx, dy, dx
|
||||||
|
|
||||||
def create_chunk_room(self, spawnable: bool = True) \
|
def create_chunk_room(self, spawnable: bool = True) \
|
||||||
-> Tuple[List[List[Tile]], int, int, int, int]:
|
-> Tuple[List[List[Tile]], int, int, int, int]:
|
||||||
"""
|
"""
|
||||||
create and return as a tile grid a room that is composed of multiple
|
Create and return as a tile grid a room that is composed of multiple
|
||||||
overlapping circles of the same radius
|
overlapping circles of the same radius
|
||||||
also return door info so we know how to place the room in the level
|
Also return door info so we know how to place the room in the level
|
||||||
"""
|
"""
|
||||||
height, width = 15, 15
|
height, width = 15, 15
|
||||||
nb_chunks, r = 6, 3
|
nb_chunks, r = 6, 3
|
||||||
|
|
||||||
h_sup, w_sup, h_off, w_off = self.corr_meta_info()
|
h_sup, w_sup, h_off, w_off = self.corr_meta_info()
|
||||||
room = [[Tile.EMPTY] * (width + w_sup) \
|
room = [[Tile.EMPTY] * (width + w_sup)
|
||||||
for _dummy in range(height + h_sup)]
|
for _dummy in range(height + h_sup)]
|
||||||
|
|
||||||
def draw_chunk(room, y, x):
|
def draw_chunk(room: List[List[Tile]], y: int, x: int) -> None:
|
||||||
for i in range(y - r, y + r + 1):
|
for i in range(y - r, y + r + 1):
|
||||||
d = (y - i)**2
|
d = (y - i)**2
|
||||||
for j in range(x - r, x + r + 1):
|
for j in range(x - r, x + r + 1):
|
||||||
if d + (x - j)**2 < r**2:
|
if d + (x - j) ** 2 < r ** 2:
|
||||||
room[i][j] = Tile.FLOOR
|
room[i][j] = Tile.FLOOR
|
||||||
|
|
||||||
draw_chunk(room, h_off + height//2 + 1, w_off + width//2 + 1)
|
draw_chunk(room, h_off + height // 2 + 1, w_off + width // 2 + 1)
|
||||||
|
|
||||||
min_w, max_w = w_off + r + 1, width + w_off - r -1
|
min_w, max_w = w_off + r + 1, width + w_off - r - 1
|
||||||
min_h, max_h = h_off + r + 1, height + h_off - r - 1
|
min_h, max_h = h_off + r + 1, height + h_off - r - 1
|
||||||
for i in range(nb_chunks):
|
for i in range(nb_chunks):
|
||||||
y, x = randint(min_h, max_h), randint(min_w, max_w)
|
y, x = randint(min_h, max_h), randint(min_w, max_w)
|
||||||
|
@ -342,42 +342,6 @@ class Generator:
|
||||||
|
|
||||||
return room, door_y, door_x, dy, dx
|
return room, door_y, door_x, dy, dx
|
||||||
|
|
||||||
def create_rectangle_room(self, spawnable: bool = True) \
|
|
||||||
-> Tuple[List[List[Tile]], int, int, int, int]:
|
|
||||||
"""
|
|
||||||
create and return as a tile grid a rectangular room
|
|
||||||
also return door info so we know how to place the room in the level
|
|
||||||
"""
|
|
||||||
|
|
||||||
shrt, lng = randint(3, 6), randint(6, 12)
|
|
||||||
if random() < .5:
|
|
||||||
height, width = shrt, lng
|
|
||||||
else:
|
|
||||||
height, width = lng, shrt
|
|
||||||
|
|
||||||
room = []
|
|
||||||
|
|
||||||
h_sup, w_sup, h_off, w_off = self.corr_meta_info()
|
|
||||||
min_w, max_w = w_off + 1, width + w_off
|
|
||||||
min_h, max_h = h_off + 1, height + h_off
|
|
||||||
for i in range(height + h_sup + 2):
|
|
||||||
room.append([])
|
|
||||||
for j in range(width + w_sup + 2):
|
|
||||||
if min_h <= i <= max_h and min_w <= j <= max_w:
|
|
||||||
room[-1].append(Tile.FLOOR)
|
|
||||||
else:
|
|
||||||
room[-1].append(Tile.EMPTY)
|
|
||||||
|
|
||||||
# log all placed tiles as spawn positions
|
|
||||||
if spawnable:
|
|
||||||
self.register_spawn_area(room)
|
|
||||||
|
|
||||||
# attach exit
|
|
||||||
door_y, door_x, dy, dx = self.attach_door(room, h_sup, w_sup,
|
|
||||||
h_off, w_off)
|
|
||||||
|
|
||||||
return room, door_y, door_x, dy, dx
|
|
||||||
|
|
||||||
def create_random_room(self, spawnable: bool = True) \
|
def create_random_room(self, spawnable: bool = True) \
|
||||||
-> Tuple[List[list], int, int, int, int]:
|
-> Tuple[List[list], int, int, int, int]:
|
||||||
"""
|
"""
|
||||||
|
@ -395,11 +359,9 @@ class Generator:
|
||||||
break
|
break
|
||||||
|
|
||||||
if key == "circular":
|
if key == "circular":
|
||||||
return self.create_circular_room(spawnable = spawnable)
|
return self.create_circular_room(spawnable=spawnable)
|
||||||
elif key == "chunks":
|
elif key == "chunks":
|
||||||
return self.create_chunk_room(spawnable = spawnable)
|
return self.create_chunk_room(spawnable=spawnable)
|
||||||
elif key == "rectangle":
|
|
||||||
return self.create_rectangle_room(spawnable = spawnable)
|
|
||||||
|
|
||||||
def register_spawn_area(self, area: List[List[Tile]]) -> None:
|
def register_spawn_area(self, area: List[List[Tile]]) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -489,6 +451,7 @@ class Generator:
|
||||||
tries += 1
|
tries += 1
|
||||||
|
|
||||||
# post-processing
|
# post-processing
|
||||||
|
self.place_walls(level)
|
||||||
|
|
||||||
# because when a room is placed, it leads to exactly one previously
|
# because when a room is placed, it leads to exactly one previously
|
||||||
# placed room, the level has a tree like structure with the starting
|
# placed room, the level has a tree like structure with the starting
|
||||||
|
@ -502,9 +465,6 @@ class Generator:
|
||||||
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)
|
||||||
|
|
||||||
# surround the floor with walls
|
|
||||||
self.place_walls(level)
|
|
||||||
|
|
||||||
# place an exit ladder
|
# place an exit ladder
|
||||||
y, x = randint(0, height - 1), randint(0, width - 1)
|
y, x = randint(0, height - 1), randint(0, width - 1)
|
||||||
while level[y][x] != Tile.FLOOR or \
|
while level[y][x] != Tile.FLOOR or \
|
||||||
|
|
|
@ -134,13 +134,13 @@ class TestEntities(unittest.TestCase):
|
||||||
self.map.remove_entity(entity2)
|
self.map.remove_entity(entity2)
|
||||||
|
|
||||||
# Test following the player and finding the player as target
|
# Test following the player and finding the player as target
|
||||||
self.player.move(5, 5)
|
self.player.move(6, 5)
|
||||||
fam.move(4, 5)
|
fam.move(5, 5)
|
||||||
fam.target = None
|
fam.target = None
|
||||||
self.player.move_down()
|
self.player.move_down()
|
||||||
self.map.tick(self.player)
|
self.map.tick(self.player)
|
||||||
self.assertTrue(fam.target == self.player)
|
self.assertTrue(fam.target == self.player)
|
||||||
self.assertEqual(fam.y, 5)
|
self.assertEqual(fam.y, 6)
|
||||||
self.assertEqual(fam.x, 5)
|
self.assertEqual(fam.x, 5)
|
||||||
|
|
||||||
# Test random move
|
# Test random move
|
||||||
|
@ -155,9 +155,9 @@ class TestEntities(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
item = Item()
|
item = Item()
|
||||||
self.map.add_entity(item)
|
self.map.add_entity(item)
|
||||||
self.assertFalse(item.held)
|
self.assertIsNone(item.held_by)
|
||||||
item.hold(self.player)
|
item.hold(self.player)
|
||||||
self.assertTrue(item.held)
|
self.assertEqual(item.held_by, self.player)
|
||||||
item.drop()
|
item.drop()
|
||||||
self.assertEqual(item.y, 1)
|
self.assertEqual(item.y, 1)
|
||||||
self.assertEqual(item.x, 6)
|
self.assertEqual(item.x, 6)
|
||||||
|
@ -165,7 +165,6 @@ class TestEntities(unittest.TestCase):
|
||||||
# Pick up item
|
# Pick up item
|
||||||
self.player.move_left()
|
self.player.move_left()
|
||||||
self.player.move_right()
|
self.player.move_right()
|
||||||
self.assertTrue(item.held)
|
|
||||||
self.assertEqual(item.held_by, self.player)
|
self.assertEqual(item.held_by, self.player)
|
||||||
self.assertIn(item, self.player.inventory)
|
self.assertIn(item, self.player.inventory)
|
||||||
self.assertNotIn(item, self.map.entities)
|
self.assertNotIn(item, self.map.entities)
|
||||||
|
@ -208,9 +207,10 @@ class TestEntities(unittest.TestCase):
|
||||||
# The player can't hold the explosion
|
# The player can't hold the explosion
|
||||||
explosion.hold(self.player)
|
explosion.hold(self.player)
|
||||||
self.assertNotIn(explosion, self.player.inventory)
|
self.assertNotIn(explosion, self.player.inventory)
|
||||||
self.assertFalse(explosion.held)
|
self.assertIsNone(explosion.held_by)
|
||||||
|
|
||||||
# The explosion disappears after one tick
|
# The explosion disappears after two ticks
|
||||||
|
explosion.act(self.map)
|
||||||
explosion.act(self.map)
|
explosion.act(self.map)
|
||||||
self.assertNotIn(explosion, self.map.entities)
|
self.assertNotIn(explosion, self.map.entities)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
import os
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from ..bootstrap import Bootstrap
|
from ..bootstrap import Bootstrap
|
||||||
|
@ -49,6 +48,7 @@ class TestGame(unittest.TestCase):
|
||||||
# Add items in the inventory to check that it is well loaded
|
# Add items in the inventory to check that it is well loaded
|
||||||
bomb.hold(self.game.player)
|
bomb.hold(self.game.player)
|
||||||
sword.hold(self.game.player)
|
sword.hold(self.game.player)
|
||||||
|
sword.equip()
|
||||||
|
|
||||||
# Ensure that merchants can be saved
|
# Ensure that merchants can be saved
|
||||||
merchant = Merchant()
|
merchant = Merchant()
|
||||||
|
@ -100,7 +100,6 @@ class TestGame(unittest.TestCase):
|
||||||
Yeah, that's only for coverage.
|
Yeah, that's only for coverage.
|
||||||
"""
|
"""
|
||||||
self.assertRaises(Exception, Bootstrap.run_game)
|
self.assertRaises(Exception, Bootstrap.run_game)
|
||||||
self.assertEqual(os.getenv("TERM", "unknown"), "unknown")
|
|
||||||
|
|
||||||
def test_key_translation(self) -> None:
|
def test_key_translation(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -256,6 +255,7 @@ class TestGame(unittest.TestCase):
|
||||||
self.game.map.add_entity(explosion)
|
self.game.map.add_entity(explosion)
|
||||||
self.assertIn(explosion, self.game.map.entities)
|
self.assertIn(explosion, self.game.map.entities)
|
||||||
self.game.handle_key_pressed(KeyValues.WAIT)
|
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()
|
rabbit = Rabbit()
|
||||||
|
@ -497,10 +497,8 @@ class TestGame(unittest.TestCase):
|
||||||
# Drop an item
|
# Drop an item
|
||||||
bomb = self.game.player.inventory[-1]
|
bomb = self.game.player.inventory[-1]
|
||||||
self.assertEqual(self.game.inventory_menu.validate(), bomb)
|
self.assertEqual(self.game.inventory_menu.validate(), bomb)
|
||||||
self.assertTrue(bomb.held)
|
|
||||||
self.assertEqual(bomb.held_by, self.game.player)
|
self.assertEqual(bomb.held_by, self.game.player)
|
||||||
self.game.handle_key_pressed(KeyValues.DROP)
|
self.game.handle_key_pressed(KeyValues.DROP)
|
||||||
self.assertFalse(bomb.held)
|
|
||||||
self.assertIsNone(bomb.held_by)
|
self.assertIsNone(bomb.held_by)
|
||||||
self.assertIsNone(bomb.owner)
|
self.assertIsNone(bomb.owner)
|
||||||
self.assertFalse(bomb.exploding)
|
self.assertFalse(bomb.exploding)
|
||||||
|
@ -510,10 +508,8 @@ class TestGame(unittest.TestCase):
|
||||||
# Use the bomb
|
# Use the bomb
|
||||||
bomb = self.game.player.inventory[-1]
|
bomb = self.game.player.inventory[-1]
|
||||||
self.assertEqual(self.game.inventory_menu.validate(), bomb)
|
self.assertEqual(self.game.inventory_menu.validate(), bomb)
|
||||||
self.assertTrue(bomb.held)
|
|
||||||
self.assertEqual(bomb.held_by, self.game.player)
|
self.assertEqual(bomb.held_by, self.game.player)
|
||||||
self.game.handle_key_pressed(KeyValues.USE)
|
self.game.handle_key_pressed(KeyValues.USE)
|
||||||
self.assertFalse(bomb.held)
|
|
||||||
self.assertIsNone(bomb.held_by)
|
self.assertIsNone(bomb.held_by)
|
||||||
self.assertEqual(bomb.owner, self.game.player)
|
self.assertEqual(bomb.owner, self.game.player)
|
||||||
self.assertTrue(bomb.exploding)
|
self.assertTrue(bomb.exploding)
|
||||||
|
@ -666,42 +662,37 @@ class TestGame(unittest.TestCase):
|
||||||
sword.hold(self.game.player)
|
sword.hold(self.game.player)
|
||||||
self.game.handle_key_pressed(KeyValues.EQUIP)
|
self.game.handle_key_pressed(KeyValues.EQUIP)
|
||||||
self.assertEqual(self.game.player.equipped_main, sword)
|
self.assertEqual(self.game.player.equipped_main, sword)
|
||||||
self.assertFalse(self.game.player.inventory)
|
|
||||||
|
|
||||||
# shield goes into the secondary equipment slot
|
# shield goes into the secondary equipment slot
|
||||||
shield = Shield()
|
shield = Shield()
|
||||||
shield.hold(self.game.player)
|
shield.hold(self.game.player)
|
||||||
self.game.handle_key_pressed(KeyValues.EQUIP)
|
shield.equip()
|
||||||
self.assertEqual(self.game.player.equipped_secondary, shield)
|
self.assertEqual(self.game.player.equipped_secondary, shield)
|
||||||
self.assertFalse(self.game.player.inventory)
|
|
||||||
|
|
||||||
# helmet goes into the helmet slot
|
# helmet goes into the helmet slot
|
||||||
helmet = Helmet()
|
helmet = Helmet()
|
||||||
helmet.hold(self.game.player)
|
helmet.hold(self.game.player)
|
||||||
self.game.handle_key_pressed(KeyValues.EQUIP)
|
helmet.equip()
|
||||||
self.assertEqual(self.game.player.equipped_helmet, helmet)
|
self.assertEqual(self.game.player.equipped_helmet, helmet)
|
||||||
self.assertFalse(self.game.player.inventory)
|
|
||||||
|
|
||||||
# helmet goes into the armor slot
|
# helmet goes into the armor slot
|
||||||
chestplate = Chestplate()
|
chestplate = Chestplate()
|
||||||
chestplate.hold(self.game.player)
|
chestplate.hold(self.game.player)
|
||||||
self.game.handle_key_pressed(KeyValues.EQUIP)
|
chestplate.equip()
|
||||||
self.assertEqual(self.game.player.equipped_armor, chestplate)
|
self.assertEqual(self.game.player.equipped_armor, chestplate)
|
||||||
self.assertFalse(self.game.player.inventory)
|
|
||||||
|
|
||||||
# Use bomb
|
# Use bomb
|
||||||
bomb = Bomb()
|
bomb = Bomb()
|
||||||
bomb.hold(self.game.player)
|
bomb.hold(self.game.player)
|
||||||
self.game.handle_key_pressed(KeyValues.EQUIP)
|
bomb.equip()
|
||||||
self.assertEqual(self.game.player.equipped_secondary, bomb)
|
self.assertEqual(self.game.player.equipped_secondary, bomb)
|
||||||
self.assertIn(shield, self.game.player.inventory)
|
self.assertFalse(shield.equipped)
|
||||||
self.game.state = GameMode.PLAY
|
self.game.state = GameMode.PLAY
|
||||||
self.game.handle_key_pressed(KeyValues.USE)
|
self.game.handle_key_pressed(KeyValues.USE)
|
||||||
self.assertIsNone(self.game.player.equipped_secondary)
|
self.assertIsNone(self.game.player.equipped_secondary)
|
||||||
self.game.state = GameMode.INVENTORY
|
self.game.state = GameMode.INVENTORY
|
||||||
self.game.handle_key_pressed(KeyValues.EQUIP)
|
shield.equip()
|
||||||
self.assertEqual(self.game.player.equipped_secondary, shield)
|
self.assertEqual(self.game.player.equipped_secondary, shield)
|
||||||
self.assertFalse(self.game.player.inventory)
|
|
||||||
|
|
||||||
# Reequip, which is useless but covers code
|
# Reequip, which is useless but covers code
|
||||||
sword.equip()
|
sword.equip()
|
||||||
|
@ -723,11 +714,13 @@ class TestGame(unittest.TestCase):
|
||||||
self.assertIn(shield, self.game.player.inventory)
|
self.assertIn(shield, self.game.player.inventory)
|
||||||
self.assertIn(helmet, self.game.player.inventory)
|
self.assertIn(helmet, self.game.player.inventory)
|
||||||
self.assertIn(chestplate, self.game.player.inventory)
|
self.assertIn(chestplate, self.game.player.inventory)
|
||||||
|
self.game.display_actions(DisplayActions.REFRESH)
|
||||||
|
|
||||||
# Test rings
|
# Test rings
|
||||||
self.game.player.inventory.clear()
|
self.game.player.inventory.clear()
|
||||||
ring = RingCritical()
|
ring = RingCritical()
|
||||||
ring.hold(self.game.player)
|
ring.hold(self.game.player)
|
||||||
|
self.game.display_actions(DisplayActions.REFRESH)
|
||||||
old_critical = self.game.player.critical
|
old_critical = self.game.player.critical
|
||||||
self.game.handle_key_pressed(KeyValues.EQUIP)
|
self.game.handle_key_pressed(KeyValues.EQUIP)
|
||||||
self.assertEqual(self.game.player.critical,
|
self.assertEqual(self.game.player.critical,
|
||||||
|
@ -951,3 +944,18 @@ class TestGame(unittest.TestCase):
|
||||||
# Exit the menu
|
# Exit the menu
|
||||||
self.game.handle_key_pressed(KeyValues.SPACE)
|
self.game.handle_key_pressed(KeyValues.SPACE)
|
||||||
self.assertEqual(self.game.state, GameMode.PLAY)
|
self.assertEqual(self.game.state, GameMode.PLAY)
|
||||||
|
|
||||||
|
def test_doors(self) -> None:
|
||||||
|
"""
|
||||||
|
Check that the user can open doors.
|
||||||
|
"""
|
||||||
|
self.game.state = GameMode.PLAY
|
||||||
|
|
||||||
|
self.game.player.move(9, 8)
|
||||||
|
self.assertEqual(self.game.map.tiles[10][8], Tile.DOOR)
|
||||||
|
# Open door
|
||||||
|
self.game.handle_key_pressed(KeyValues.DOWN)
|
||||||
|
self.assertEqual(self.game.map.tiles[10][8], Tile.FLOOR)
|
||||||
|
self.assertEqual(self.game.player.y, 10)
|
||||||
|
self.assertEqual(self.game.player.x, 8)
|
||||||
|
self.game.display_actions(DisplayActions.REFRESH)
|
||||||
|
|
|
@ -26,16 +26,17 @@ class TestBroguelike(unittest.TestCase):
|
||||||
|
|
||||||
def is_connex(self, grid: List[List[Tile]]) -> bool:
|
def is_connex(self, grid: List[List[Tile]]) -> bool:
|
||||||
h, w = len(grid), len(grid[0])
|
h, w = len(grid), len(grid[0])
|
||||||
y, x = randint(0, h - 1), randint(0, w - 1)
|
y, x = -1, -1
|
||||||
while not (grid[y][x].can_walk()):
|
while not grid[y][x].can_walk():
|
||||||
y, x = randint(0, h - 1), randint(0, w - 1)
|
y, x = randint(0, h - 1), randint(0, w - 1)
|
||||||
queue = Map.neighbourhood(grid, y, x)
|
queue = Map.neighbourhood(grid, y, x)
|
||||||
while queue:
|
while queue:
|
||||||
y, x = queue.pop()
|
y, x = queue.pop()
|
||||||
if grid[y][x].can_walk():
|
if grid[y][x].can_walk() or grid[y][x] == Tile.DOOR:
|
||||||
grid[y][x] = Tile.WALL
|
grid[y][x] = Tile.WALL
|
||||||
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() or t == Tile.DOOR
|
||||||
|
for row in grid for t in row])
|
||||||
|
|
||||||
def test_build_doors(self) -> None:
|
def test_build_doors(self) -> None:
|
||||||
m = self.stom(". .\n. .\n. .\n")
|
m = self.stom(". .\n. .\n. .\n")
|
||||||
|
|
|
@ -25,6 +25,8 @@ class Translator:
|
||||||
Loads compiled translations.
|
Loads compiled translations.
|
||||||
"""
|
"""
|
||||||
for language in cls.SUPPORTED_LOCALES:
|
for language in cls.SUPPORTED_LOCALES:
|
||||||
|
if language == "en":
|
||||||
|
continue
|
||||||
rep = Path(__file__).parent / "locale" / language / "LC_MESSAGES"
|
rep = Path(__file__).parent / "locale" / language / "LC_MESSAGES"
|
||||||
rep.mkdir(parents=True) if not rep.is_dir() else None
|
rep.mkdir(parents=True) if not rep.is_dir() else None
|
||||||
if os.path.isfile(rep / "squirrelbattle.mo"):
|
if os.path.isfile(rep / "squirrelbattle.mo"):
|
||||||
|
@ -65,7 +67,7 @@ class Translator:
|
||||||
args = ["xargs", "xgettext", "--from-code", "utf-8",
|
args = ["xargs", "xgettext", "--from-code", "utf-8",
|
||||||
"--add-comments",
|
"--add-comments",
|
||||||
"--package-name=squirrelbattle",
|
"--package-name=squirrelbattle",
|
||||||
"--package-version=3.14.1",
|
"--package-version=23.14",
|
||||||
"--copyright-holder=ÿnérant, eichhornchen, "
|
"--copyright-holder=ÿnérant, eichhornchen, "
|
||||||
"nicomarg, charlse, ifugao",
|
"nicomarg, charlse, ifugao",
|
||||||
"--msgid-bugs-address=squirrel-battle@crans.org",
|
"--msgid-bugs-address=squirrel-battle@crans.org",
|
||||||
|
|
Loading…
Reference in New Issue