Merge branch 'body-snatch' into 'master'

Body snatch

Closes #34 et #33

See merge request ynerant/squirrel-battle!38
This commit is contained in:
ynerant 2020-12-05 14:52:47 +01:00
commit 6fa11d9dfe
12 changed files with 138 additions and 50 deletions

View File

@ -109,7 +109,7 @@ class InventoryDisplay(MenuDisplay):
rep = self.pack[item.name.upper()]
selection = f"[{rep}]" if i == self.menu.position else f" {rep} "
self.addstr(self.pad, 2 + i, 0, selection
+ " " + _(item.name).capitalize())
+ " " + item.translated_name.capitalize())
@property
def truewidth(self) -> int:

View File

@ -56,6 +56,7 @@ TexturePack.ASCII_PACK = TexturePack(
RABBIT='Y',
TIGER='n',
TEDDY_BEAR='8',
BODY_SNATCH_POTION='S',
)
TexturePack.SQUIRREL_PACK = TexturePack(
@ -75,4 +76,5 @@ TexturePack.SQUIRREL_PACK = TexturePack(
RABBIT='🐇',
TIGER='🐅',
TEDDY_BEAR='🧸',
BODY_SNATCH_POTION='🔀',
)

View File

@ -1,7 +1,7 @@
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
# SPDX-License-Identifier: GPL-3.0-or-later
from random import randint
from random import choice, randint
from typing import Optional
from .player import Player
@ -67,8 +67,8 @@ class Heart(Item):
"""
healing: int
def __init__(self, healing: int = 5, *args, **kwargs):
super().__init__(name="heart", *args, **kwargs)
def __init__(self, name: str = "heart", healing: int = 5, *args, **kwargs):
super().__init__(name=name, *args, **kwargs)
self.healing = healing
def hold(self, player: "Player") -> None:
@ -96,9 +96,9 @@ class Bomb(Item):
owner: Optional["Player"]
tick: int
def __init__(self, damage: int = 5, exploding: bool = False,
*args, **kwargs):
super().__init__(name="bomb", *args, **kwargs)
def __init__(self, name: str = "bomb", damage: int = 5,
exploding: bool = False, *args, **kwargs):
super().__init__(name=name, *args, **kwargs)
self.damage = damage
self.exploding = exploding
self.tick = 4
@ -143,3 +143,36 @@ class Bomb(Item):
d["exploding"] = self.exploding
d["damage"] = self.damage
return d
class BodySnatchPotion(Item):
"""
The body-snatch potion allows to exchange all characteristics with a random
other entity.
"""
def __init__(self, name: str = "body_snatch_potion", *args, **kwargs):
super().__init__(name=name, *args, **kwargs)
def use(self) -> None:
"""
Find a valid random entity, then exchange characteristics.
"""
valid_entities = self.held_by.map.find_entities(FightingEntity)
valid_entities.remove(self.held_by)
entity = choice(valid_entities)
entity_state = entity.save_state()
player_state = self.held_by.save_state()
self.held_by.__dict__.update(entity_state)
entity.__dict__.update(player_state)
self.held_by.map.currenty, self.held_by.map.currentx = self.held_by.y,\
self.held_by.x
self.held_by.map.logs.add_message(
_("{player} exchanged its body with {entity}.").format(
player=self.held_by.translated_name.capitalize(),
entity=entity.translated_name))
self.held_by.recalculate_paths()
self.held_by.inventory.remove(self)

View File

@ -63,9 +63,9 @@ class Tiger(Monster):
"""
A tiger monster
"""
def __init__(self, strength: int = 2, maxhealth: int = 20,
*args, **kwargs) -> None:
super().__init__(name="tiger", strength=strength,
def __init__(self, name: str = "tiger", strength: int = 2,
maxhealth: int = 20, *args, **kwargs) -> None:
super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs)
@ -73,9 +73,9 @@ class Hedgehog(Monster):
"""
A really mean hedgehog monster
"""
def __init__(self, strength: int = 3, maxhealth: int = 10,
*args, **kwargs) -> None:
super().__init__(name="hedgehog", strength=strength,
def __init__(self, name: str = "hedgehog", strength: int = 3,
maxhealth: int = 10, *args, **kwargs) -> None:
super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs)
@ -83,9 +83,9 @@ class Rabbit(Monster):
"""
A rabbit monster
"""
def __init__(self, strength: int = 1, maxhealth: int = 15,
*args, **kwargs) -> None:
super().__init__(name="rabbit", strength=strength,
def __init__(self, name: str = "rabbit", strength: int = 1,
maxhealth: int = 15, *args, **kwargs) -> None:
super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs)
@ -93,7 +93,7 @@ class TeddyBear(Monster):
"""
A cute teddybear monster
"""
def __init__(self, strength: int = 0, maxhealth: int = 50,
*args, **kwargs) -> None:
super().__init__(name="teddy_bear", strength=strength,
def __init__(self, name: str = "teddy_bear", strength: int = 0,
maxhealth: int = 50, *args, **kwargs) -> None:
super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs)

View File

@ -16,17 +16,24 @@ class Player(FightingEntity):
inventory: list
paths: Dict[Tuple[int, int], Tuple[int, int]]
def __init__(self, maxhealth: int = 20, strength: int = 5,
intelligence: int = 1, charisma: int = 1, dexterity: int = 1,
constitution: int = 1, level: int = 1, current_xp: int = 0,
max_xp: int = 10, *args, **kwargs) -> None:
super().__init__(name="player", maxhealth=maxhealth, strength=strength,
def __init__(self, name: str = "player", maxhealth: int = 20,
strength: int = 5, intelligence: int = 1, charisma: int = 1,
dexterity: int = 1, constitution: int = 1, level: int = 1,
current_xp: int = 0, max_xp: int = 10, inventory: list = None,
*args, **kwargs) \
-> None:
super().__init__(name=name, maxhealth=maxhealth, strength=strength,
intelligence=intelligence, charisma=charisma,
dexterity=dexterity, constitution=constitution,
level=level, *args, **kwargs)
self.current_xp = current_xp
self.max_xp = max_xp
self.inventory = list()
self.inventory = inventory if inventory else list()
for i in range(len(self.inventory)):
if isinstance(self.inventory[i], dict):
entity_classes = self.get_all_entity_classes_in_a_dict()
item_class = entity_classes[self.inventory[i]["type"]]
self.inventory[i] = item_class(**self.inventory[i])
self.paths = dict()
def move(self, y: int, x: int) -> None:
@ -117,4 +124,5 @@ class Player(FightingEntity):
d = super().save_state()
d["current_xp"] = self.current_xp
d["max_xp"] = self.max_xp
d["inventory"] = [item.save_state() for item in self.inventory]
return d

View File

@ -48,7 +48,7 @@ class Game:
Create a new game on the screen.
"""
# TODO generate a new map procedurally
self.map = Map.load(ResourceManager.get_asset_path("example_map_2.txt"))
self.map = Map.load(ResourceManager.get_asset_path("example_map.txt"))
self.map.logs = self.logs
self.logs.clear()
self.player = Player()

View File

@ -324,10 +324,11 @@ class Entity:
"""
Returns all entities subclasses
"""
from squirrelbattle.entities.items import Heart, Bomb
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart
from squirrelbattle.entities.monsters import Tiger, Hedgehog, \
Rabbit, TeddyBear
return [Tiger, Bomb, Heart, Hedgehog, Rabbit, TeddyBear]
return [BodySnatchPotion, Bomb, Heart, Hedgehog,
Rabbit, TeddyBear, Tiger]
@staticmethod
def get_all_entity_classes_in_a_dict() -> dict:
@ -337,11 +338,12 @@ class Entity:
from squirrelbattle.entities.player import Player
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \
TeddyBear
from squirrelbattle.entities.items import Bomb, Heart
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart
return {
"Tiger": Tiger,
"Bomb": Bomb,
"Heart": Heart,
"BodySnatchPotion": BodySnatchPotion,
"Hedgehog": Hedgehog,
"Rabbit": Rabbit,
"TeddyBear": TeddyBear,
@ -423,7 +425,7 @@ class FightingEntity(Entity):
"""
Returns a fighting entities specific attributes
"""
return ["maxhealth", "health", "level", "strength",
return ["name", "maxhealth", "health", "level", "strength",
"intelligence", "charisma", "dexterity", "constitution"]
def save_state(self) -> dict:

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: squirrelbattle 3.14.1\n"
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
"POT-Creation-Date: 2020-12-05 13:11+0100\n"
"POT-Creation-Date: 2020-12-05 14:46+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"
@ -25,16 +25,21 @@ msgstr "== BESTAND =="
msgid "Inventory:"
msgstr "Bestand:"
#: squirrelbattle/display/statsdisplay.py:39
#: squirrelbattle/display/statsdisplay.py:50
msgid "YOU ARE DEAD"
msgstr "SIE WURDEN GESTORBEN"
#. The bomb is exploding.
#. Each entity that is close to the bomb takes damages.
#. The player earn XP if the entity was killed.
#: squirrelbattle/entities/items.py:129
#: squirrelbattle/entities/items.py:128
msgid "Bomb is exploding."
msgstr ""
msgstr "Die Bombe explodiert."
#: squirrelbattle/entities/items.py:172
#, python-brace-format
msgid "{player} exchanged its body with {entity}."
msgstr "{player} täuscht seinem Körper mit {entity} aus."
#: squirrelbattle/game.py:177
msgid ""
@ -60,17 +65,17 @@ msgstr ""
"Die JSON-Datei ist nicht korrekt.\n"
"Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht."
#: squirrelbattle/interfaces.py:398
#: squirrelbattle/interfaces.py:400
#, python-brace-format
msgid "{name} hits {opponent}."
msgstr "{name} schlägt {opponent}."
#: squirrelbattle/interfaces.py:410
#: squirrelbattle/interfaces.py:412
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} nimmt {amount} Schadenspunkte."
#: squirrelbattle/interfaces.py:412
#: squirrelbattle/interfaces.py:414
#, python-brace-format
msgid "{name} dies."
msgstr "{name} stirbt."
@ -79,8 +84,8 @@ msgstr "{name} stirbt."
msgid "Back"
msgstr "Zurück"
#: squirrelbattle/tests/game_test.py:294 squirrelbattle/tests/game_test.py:297
#: squirrelbattle/tests/game_test.py:300
#: squirrelbattle/tests/game_test.py:300 squirrelbattle/tests/game_test.py:303
#: squirrelbattle/tests/game_test.py:306
#: squirrelbattle/tests/translations_test.py:16
msgid "New game"
msgstr "Neu Spiel"
@ -186,9 +191,13 @@ msgid "teddy bear"
msgstr "Teddybär"
#: squirrelbattle/tests/translations_test.py:64
msgid "body snatch potion"
msgstr "Leichenfleddererzaubertrank"
#: squirrelbattle/tests/translations_test.py:65
msgid "bomb"
msgstr "Bombe"
#: squirrelbattle/tests/translations_test.py:65
#: squirrelbattle/tests/translations_test.py:66
msgid "heart"
msgstr "Herz"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: squirrelbattle 3.14.1\n"
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
"POT-Creation-Date: 2020-12-05 13:11+0100\n"
"POT-Creation-Date: 2020-12-05 14:46+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"
@ -25,17 +25,22 @@ msgstr "== INVENTAIRE =="
msgid "Inventory:"
msgstr "Inventaire :"
#: squirrelbattle/display/statsdisplay.py:39
#: squirrelbattle/display/statsdisplay.py:50
msgid "YOU ARE DEAD"
msgstr "VOUS ÊTES MORT"
#. The bomb is exploding.
#. Each entity that is close to the bomb takes damages.
#. The player earn XP if the entity was killed.
#: squirrelbattle/entities/items.py:129
#: squirrelbattle/entities/items.py:128
msgid "Bomb is exploding."
msgstr "La bombe explose."
#: squirrelbattle/entities/items.py:172
#, python-brace-format
msgid "{player} exchanged its body with {entity}."
msgstr "{player} a échangé son corps avec {entity}."
#: squirrelbattle/game.py:177
msgid ""
"Some keys are missing in your save file.\n"
@ -60,17 +65,17 @@ msgstr ""
"Le fichier JSON de sauvegarde est incorrect.\n"
"Votre sauvegarde semble corrompue. Elle a été supprimée."
#: squirrelbattle/interfaces.py:398
#: squirrelbattle/interfaces.py:400
#, python-brace-format
msgid "{name} hits {opponent}."
msgstr "{name} frappe {opponent}."
#: squirrelbattle/interfaces.py:410
#: squirrelbattle/interfaces.py:412
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} prend {amount} points de dégât."
#: squirrelbattle/interfaces.py:412
#: squirrelbattle/interfaces.py:414
#, python-brace-format
msgid "{name} dies."
msgstr "{name} meurt."
@ -79,8 +84,8 @@ msgstr "{name} meurt."
msgid "Back"
msgstr "Retour"
#: squirrelbattle/tests/game_test.py:294 squirrelbattle/tests/game_test.py:297
#: squirrelbattle/tests/game_test.py:300
#: squirrelbattle/tests/game_test.py:300 squirrelbattle/tests/game_test.py:303
#: squirrelbattle/tests/game_test.py:306
#: squirrelbattle/tests/translations_test.py:16
msgid "New game"
msgstr "Nouvelle partie"
@ -186,9 +191,13 @@ msgid "teddy bear"
msgstr "nounours"
#: squirrelbattle/tests/translations_test.py:64
msgid "body snatch potion"
msgstr "potion d'arrachage de corps"
#: squirrelbattle/tests/translations_test.py:65
msgid "bomb"
msgstr "bombe"
#: squirrelbattle/tests/translations_test.py:65
#: squirrelbattle/tests/translations_test.py:66
msgid "heart"
msgstr "cœur"

View File

@ -3,7 +3,7 @@
import unittest
from squirrelbattle.entities.items import Bomb, Heart, Item
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart, Item
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, TeddyBear
from squirrelbattle.entities.player import Player
from squirrelbattle.interfaces import Entity, Map
@ -154,6 +154,24 @@ class TestEntities(unittest.TestCase):
heart_state = item.save_state()
self.assertEqual(heart_state["healing"], item.healing)
def test_body_snatch_potion(self) -> None:
"""
Test some random stuff with body snatch potions.
"""
item = BodySnatchPotion()
self.map.add_entity(item)
item.hold(self.player)
tiger = Tiger(y=42, x=42)
self.map.add_entity(tiger)
# The player becomes a tiger, and the tiger becomes a squirrel
item.use()
self.assertEqual(self.player.name, "tiger")
self.assertEqual(tiger.name, "player")
self.assertEqual(self.player.y, 42)
self.assertEqual(self.player.x, 42)
def test_players(self) -> None:
"""
Test some random stuff with players.

View File

@ -32,6 +32,9 @@ class TestGame(unittest.TestCase):
"""
Save a game and reload it.
"""
bomb = Bomb()
self.game.map.add_entity(bomb)
bomb.hold(self.game.player)
old_state = self.game.save_state()
self.game.handle_key_pressed(KeyValues.DOWN)
@ -45,6 +48,9 @@ class TestGame(unittest.TestCase):
new_state = self.game.save_state()
self.assertEqual(old_state, new_state)
# Ensure that the bomb is loaded
self.assertTrue(self.game.player.inventory)
# Error on loading save
with open(ResourceManager.get_config_path("save.json"), "w") as f:
f.write("I am not a JSON file")

View File

@ -61,5 +61,6 @@ class TestTranslations(unittest.TestCase):
self.assertEqual(_("rabbit"), "lapin")
self.assertEqual(_("teddy bear"), "nounours")
self.assertEqual(_("body snatch potion"), "potion d'arrachage de corps")
self.assertEqual(_("bomb"), "bombe")
self.assertEqual(_("heart"), "cœur")