Merge branch 'equipment' into 'master'

Equipment

Closes #19, #30, #48, #51 et #52

See merge request ynerant/squirrel-battle!54
This commit is contained in:
ynerant 2021-01-08 02:19:29 +01:00
commit fb47c15d6b
12 changed files with 545 additions and 74 deletions

View File

@ -33,7 +33,8 @@ class StatsDisplay(Display):
f"INT {self.player.intelligence}\n" \
f"CHR {self.player.charisma}\n" \
f"DEX {self.player.dexterity}\n" \
f"CON {self.player.constitution}"
f"CON {self.player.constitution}\n" \
f"CRI {self.player.critical}%"
self.addstr(self.pad, 3, 0, string3)
inventory_str = _("Inventory:") + " "
@ -49,13 +50,31 @@ class StatsDisplay(Display):
if count > 1:
inventory_str += f"x{count} "
printed_items.append(item)
self.addstr(self.pad, 8, 0, inventory_str)
self.addstr(self.pad, 9, 0, inventory_str)
self.addstr(self.pad, 9, 0, f"{self.pack.HAZELNUT} "
f"x{self.player.hazel}")
if self.player.equipped_main:
self.addstr(self.pad, 10, 0,
_("Equipped main:") + " "
f"{self.pack[self.player.equipped_main.name.upper()]}")
if self.player.equipped_secondary:
self.addstr(self.pad, 11, 0,
_("Equipped secondary:") + " "
+ self.pack[self.player.equipped_secondary
.name.upper()])
if self.player.equipped_armor:
self.addstr(self.pad, 12, 0,
_("Equipped chestplate:") + " "
+ self.pack[self.player.equipped_armor.name.upper()])
if self.player.equipped_helmet:
self.addstr(self.pad, 13, 0,
_("Equipped helmet:") + " "
+ self.pack[self.player.equipped_helmet.name.upper()])
self.addstr(self.pad, 14, 0, f"{self.pack.HAZELNUT} "
f"x{self.player.hazel}")
if self.player.dead:
self.addstr(self.pad, 11, 0, _("YOU ARE DEAD"), curses.COLOR_RED,
self.addstr(self.pad, 15, 0, _("YOU ARE DEAD"), curses.COLOR_RED,
bold=True, blink=True, standout=True)
def display(self) -> None:

View File

@ -35,6 +35,12 @@ class TexturePack:
TIGER: str
TRUMPET: str
WALL: str
EAGLE: str
SHIELD: str
CHESTPLATE: str
HELMET: str
RING_OF_MORE_EXPERIENCE: str
RING_OF_CRITICAL_DAMAGE: str
ASCII_PACK: "TexturePack"
SQUIRREL_PACK: "TexturePack"
@ -66,7 +72,7 @@ TexturePack.ASCII_PACK = TexturePack(
entity_bg_color=curses.COLOR_BLACK,
BODY_SNATCH_POTION='S',
BOMB='o',
BOMB='ç',
EMPTY=' ',
EXPLOSION='%',
FLOOR='.',
@ -77,12 +83,18 @@ TexturePack.ASCII_PACK = TexturePack(
MERCHANT='M',
PLAYER='@',
RABBIT='Y',
SHIELD='D',
SUNFLOWER='I',
SWORD='\u2020',
TEDDY_BEAR='8',
TIGER='n',
TRUMPET='/',
WALL='#',
EAGLE='µ',
CHESTPLATE='(',
HELMET='0',
RING_OF_MORE_EXPERIENCE='o',
RING_OF_CRITICAL_DAMAGE='o',
)
TexturePack.SQUIRREL_PACK = TexturePack(
@ -107,10 +119,16 @@ TexturePack.SQUIRREL_PACK = TexturePack(
PLAYER='🐿️ ',
MERCHANT='🦜',
RABBIT='🐇',
SHIELD='🛡️ ',
SUNFLOWER='🌻',
SWORD='🗡️ ',
TEDDY_BEAR='🧸',
TIGER='🐅',
TRUMPET='🎺',
WALL='🧱',
EAGLE='🦅',
CHESTPLATE='🦺',
HELMET='⛑️',
RING_OF_MORE_EXPERIENCE='💍',
RING_OF_CRITICAL_DAMAGE='💍',
)

View File

@ -4,7 +4,6 @@
from random import choice, randint
from typing import Optional
from .player import Player
from ..interfaces import Entity, FightingEntity, Map, InventoryHolder
from ..translations import gettext as _
@ -30,7 +29,7 @@ class Item(Entity):
The item is dropped from the inventory onto the floor.
"""
if self.held:
self.held_by.inventory.remove(self)
self.held_by.remove_from_inventory(self)
self.held_by.map.add_entity(self)
self.move(self.held_by.y, self.held_by.x)
self.held = False
@ -45,15 +44,27 @@ class Item(Entity):
"""
Indicates what should be done when the item is equipped.
"""
# Other objects are only equipped as secondary.
if self.held_by.equipped_secondary:
self.held_by.equipped_secondary.unequip()
self.held_by.remove_from_inventory(self)
self.held_by.equipped_secondary = self
def hold(self, player: InventoryHolder) -> None:
def unequip(self) -> None:
"""
Indicates what should be done when the item is unequipped.
"""
self.held_by.remove_from_inventory(self)
self.held_by.add_to_inventory(self)
def hold(self, holder: InventoryHolder) -> None:
"""
The item is taken from the floor and put into the inventory.
"""
self.held = True
self.held_by = player
self.held_by = holder
self.held_by.map.remove_entity(self)
player.add_to_inventory(self)
holder.add_to_inventory(self)
def save_state(self) -> dict:
"""
@ -68,7 +79,8 @@ class Item(Entity):
"""
Returns the list of all item classes.
"""
return [BodySnatchPotion, Bomb, Heart, Sword]
return [BodySnatchPotion, Bomb, Heart, Shield, Sword,
Chestplate, Helmet, RingCritical, RingXP]
def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool:
"""
@ -120,7 +132,7 @@ class Bomb(Item):
"""
damage: int = 5
exploding: bool
owner: Optional["Player"]
owner: Optional["InventoryHolder"]
tick: int
def __init__(self, name: str = "bomb", damage: int = 5,
@ -193,7 +205,6 @@ class Explosion(Item):
"""
The player can't hold an explosion.
"""
pass
class Weapon(Item):
@ -214,14 +225,99 @@ class Weapon(Item):
d["damage"] = self.damage
return d
def equip(self) -> None:
"""
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
def unequip(self) -> None:
"""
Remove the strength earned by the weapon.
:return:
"""
super().unequip()
self.held_by.strength -= self.damage
class Sword(Weapon):
"""
A basic weapon
"""
def __init__(self, name: str = "sword", price: int = 20, *args, **kwargs):
def __init__(self, name: str = "sword", price: int = 20,
*args, **kwargs):
super().__init__(name=name, price=price, *args, **kwargs)
self.name = name
class Armor(Item):
"""
Class of items that increase the player's constitution.
"""
constitution: int
def __init__(self, constitution: int, *args, **kwargs):
super().__init__(*args, **kwargs)
self.constitution = constitution
def equip(self) -> None:
super().equip()
self.held_by.constitution += self.constitution
def unequip(self) -> None:
super().unequip()
self.held_by.constitution -= self.constitution
def save_state(self) -> dict:
d = super().save_state()
d["constitution"] = self.constitution
return d
class Shield(Armor):
"""
Class of shield items, they can be equipped in the other hand.
"""
def __init__(self, name: str = "shield", constitution: int = 2,
price: int = 6, *args, **kwargs):
super().__init__(name=name, constitution=constitution, price=price,
*args, **kwargs)
class Helmet(Armor):
"""
Class of helmet items, they can be equipped on the head.
"""
def __init__(self, name: str = "helmet", constitution: int = 2,
price: int = 8, *args, **kwargs):
super().__init__(name=name, constitution=constitution, price=price,
*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 of chestplate items, they can be equipped on the body.
"""
def __init__(self, name: str = "chestplate", constitution: int = 4,
price: int = 15, *args, **kwargs):
super().__init__(name=name, constitution=constitution, price=price,
*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):
@ -256,3 +352,79 @@ class BodySnatchPotion(Item):
self.held_by.recalculate_paths()
self.held_by.inventory.remove(self)
class Ring(Item):
"""
A class of rings that boost the player's statistics.
"""
maxhealth: int
strength: int
intelligence: int
charisma: int
dexterity: int
constitution: int
critical: int
experience: float
def __init__(self, maxhealth: int = 0, strength: int = 0,
intelligence: int = 0, charisma: int = 0,
dexterity: int = 0, constitution: int = 0,
critical: int = 0, experience: float = 0, *args, **kwargs):
super().__init__(*args, **kwargs)
self.maxhealth = maxhealth
self.strength = strength
self.intelligence = intelligence
self.charisma = charisma
self.dexterity = dexterity
self.constitution = constitution
self.critical = critical
self.experience = experience
def equip(self) -> None:
super().equip()
self.held_by.maxhealth += self.maxhealth
self.held_by.strength += self.strength
self.held_by.intelligence += self.intelligence
self.held_by.charisma += self.charisma
self.held_by.dexterity += self.dexterity
self.held_by.constitution += self.constitution
self.held_by.critical += self.critical
self.held_by.xp_buff += self.experience
def unequip(self) -> None:
super().unequip()
self.held_by.maxhealth -= self.maxhealth
self.held_by.strength -= self.strength
self.held_by.intelligence -= self.intelligence
self.held_by.charisma -= self.charisma
self.held_by.dexterity -= self.dexterity
self.held_by.constitution -= self.constitution
self.held_by.critical -= self.critical
self.held_by.xp_buff -= self.experience
def save_state(self) -> dict:
d = super().save_state()
d["maxhealth"] = self.maxhealth
d["strength"] = self.strength
d["intelligence"] = self.intelligence
d["charisma"] = self.charisma
d["dexterity"] = self.dexterity
d["constitution"] = self.constitution
d["critical"] = self.critical
d["experience"] = self.experience
return d
class RingCritical(Ring):
def __init__(self, name: str = "ring_of_critical_damage", price: int = 15,
critical: int = 20, *args, **kwargs):
super().__init__(name=name, price=price, critical=critical,
*args, **kwargs)
class RingXP(Ring):
def __init__(self, name: str = "ring_of_more_experience", price: int = 25,
experience: float = 2, *args, **kwargs):
super().__init__(name=name, price=price, experience=experience,
*args, **kwargs)

View File

@ -94,9 +94,11 @@ class Rabbit(Monster):
A rabbit monster.
"""
def __init__(self, name: str = "rabbit", strength: int = 1,
maxhealth: int = 15, *args, **kwargs) -> None:
maxhealth: int = 15, critical: int = 30,
*args, **kwargs) -> None:
super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs)
maxhealth=maxhealth, critical=critical,
*args, **kwargs)
class TeddyBear(Monster):
@ -107,3 +109,13 @@ class TeddyBear(Monster):
maxhealth: int = 50, *args, **kwargs) -> None:
super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs)
class GiantSeaEagle(Monster):
"""
An eagle boss
"""
def __init__(self, name: str = "eagle", strength: int = 1000,
maxhealth: int = 5000, *args, **kwargs) -> None:
super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs)

View File

@ -2,7 +2,9 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from random import randint
from typing import Dict, Optional, Tuple
from .items import Item
from ..interfaces import FightingEntity, InventoryHolder
@ -12,22 +14,40 @@ class Player(InventoryHolder, FightingEntity):
"""
current_xp: int = 0
max_xp: int = 10
xp_buff: float = 1
paths: Dict[Tuple[int, int], Tuple[int, int]]
equipped_main: Optional[Item]
equipped_secondary: Optional[Item]
equipped_helmet: Optional[Item]
equipped_armor: Optional[Item]
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,
hazel: int = 42, vision: int = 5, *args, **kwargs) \
-> None:
hazel: int = 42, equipped_main: Optional[Item] = None,
equipped_armor: Optional[Item] = None, critical: int = 5,
equipped_secondary: Optional[Item] = None,
equipped_helmet: Optional[Item] = None, xp_buff: float = 1,
vision: int = 5, *args, **kwargs) -> None:
super().__init__(name=name, maxhealth=maxhealth, strength=strength,
intelligence=intelligence, charisma=charisma,
dexterity=dexterity, constitution=constitution,
level=level, *args, **kwargs)
level=level, critical=critical, *args, **kwargs)
self.current_xp = current_xp
self.max_xp = max_xp
self.xp_buff = xp_buff
self.inventory = self.translate_inventory(inventory or [])
self.paths = dict()
self.hazel = hazel
self.equipped_main = self.dict_to_item(equipped_main) \
if isinstance(equipped_main, dict) else equipped_main
self.equipped_armor = self.dict_to_item(equipped_armor) \
if isinstance(equipped_armor, dict) else equipped_armor
self.equipped_secondary = self.dict_to_item(equipped_secondary) \
if isinstance(equipped_secondary, dict) else equipped_secondary
self.equipped_helmet = self.dict_to_item(equipped_helmet) \
if isinstance(equipped_helmet, dict) else equipped_helmet
self.vision = vision
def move(self, y: int, x: int) -> None:
@ -60,9 +80,24 @@ class Player(InventoryHolder, FightingEntity):
Adds some experience to the player.
If the required amount is reached, the player levels up.
"""
self.current_xp += xp
self.current_xp += int(xp * self.xp_buff)
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
def check_move(self, y: int, x: int, move_if_possible: bool = False) \
-> bool:
@ -92,4 +127,12 @@ class Player(InventoryHolder, FightingEntity):
d = super().save_state()
d["current_xp"] = self.current_xp
d["max_xp"] = self.max_xp
d["equipped_main"] = self.equipped_main.save_state()\
if self.equipped_main else None
d["equipped_armor"] = self.equipped_armor.save_state()\
if self.equipped_armor else None
d["equipped_secondary"] = self.equipped_secondary.save_state()\
if self.equipped_secondary else None
d["equipped_helmet"] = self.equipped_helmet.save_state()\
if self.equipped_helmet else None
return d

View File

@ -131,7 +131,7 @@ class Game:
self.state = GameMode.MAINMENU
self.display_actions(DisplayActions.REFRESH)
def handle_key_pressed_play(self, key: KeyValues) -> None:
def handle_key_pressed_play(self, key: KeyValues) -> None: # noqa: C901
"""
In play mode, arrows or zqsd move the main character.
"""
@ -149,6 +149,12 @@ class Game:
self.map.tick(self.player)
elif key == KeyValues.INVENTORY:
self.state = GameMode.INVENTORY
self.display_actions(DisplayActions.UPDATE)
elif key == KeyValues.USE and self.player.equipped_main:
if self.player.equipped_main:
self.player.equipped_main.use()
if self.player.equipped_secondary:
self.player.equipped_secondary.use()
elif key == KeyValues.SPACE:
self.state = GameMode.MAINMENU
elif key == KeyValues.CHAT:

View File

@ -3,7 +3,7 @@
from enum import Enum, auto
from math import ceil, sqrt
from random import choice, randint
from random import choice, choices, randint
from typing import List, Optional, Any, Dict, Tuple
from queue import PriorityQueue
from functools import reduce
@ -188,7 +188,8 @@ class Map:
tile = self.tiles[y][x]
if tile.can_walk():
break
entity = choice(Entity.get_all_entity_classes())()
entity = choices(Entity.get_all_entity_classes(),
weights=Entity.get_weights(), k=1)[0]()
entity.move(y, x)
self.add_entity(entity)
@ -602,11 +603,19 @@ class Entity:
"""
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart
from squirrelbattle.entities.monsters import Tiger, Hedgehog, \
Rabbit, TeddyBear
from squirrelbattle.entities.friendly import Merchant, Sunflower, \
Trumpet
Rabbit, TeddyBear, GiantSeaEagle
from squirrelbattle.entities.friendly import Merchant, Sunflower
return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear,
Sunflower, Tiger, Merchant, Trumpet]
Sunflower, Tiger, Merchant, GiantSeaEagle]
@staticmethod
def get_weights() -> list:
"""
Returns a weigth list associated to the above function, to
be used to spawn random entities with a certain probability.
"""
return [3, 5, 6, 5, 5, 5,
5, 4, 4, 1]
@staticmethod
def get_all_entity_classes_in_a_dict() -> dict:
@ -615,11 +624,11 @@ class Entity:
"""
from squirrelbattle.entities.player import Player
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \
TeddyBear
TeddyBear, GiantSeaEagle
from squirrelbattle.entities.friendly import Merchant, Sunflower, \
Trumpet
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \
Heart, Sword
Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP
return {
"Tiger": Tiger,
"Bomb": Bomb,
@ -633,6 +642,12 @@ class Entity:
"Sunflower": Sunflower,
"Sword": Sword,
"Trumpet": Trumpet,
"Eagle": GiantSeaEagle,
"Shield": Shield,
"Chestplate": Chestplate,
"Helmet": Helmet,
"RingCritical": RingCritical,
"RingXP": RingXP,
}
def save_state(self) -> dict:
@ -659,11 +674,12 @@ class FightingEntity(Entity):
dexterity: int
constitution: int
level: int
critical: int
def __init__(self, maxhealth: int = 0, health: Optional[int] = None,
strength: int = 0, intelligence: int = 0, charisma: int = 0,
dexterity: int = 0, constitution: int = 0, level: int = 0,
*args, **kwargs) -> None:
critical: int = 0, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.maxhealth = maxhealth
self.health = maxhealth if health is None else health
@ -673,6 +689,7 @@ class FightingEntity(Entity):
self.dexterity = dexterity
self.constitution = constitution
self.level = level
self.critical = critical
@property
def dead(self) -> bool:
@ -686,21 +703,28 @@ class FightingEntity(Entity):
The entity deals damage to the opponent
based on their respective stats.
"""
diceroll = randint(1, 100)
damage = self.strength
string = " "
if diceroll <= self.critical: # It is a critical hit
damage *= 4
string = " " + _("It's a critical hit!") + " "
return _("{name} hits {opponent}.")\
.format(name=_(self.translated_name.capitalize()),
opponent=_(opponent.translated_name)) + " " + \
opponent.take_damage(self, self.strength)
opponent=_(opponent.translated_name)) + string + \
opponent.take_damage(self, damage)
def take_damage(self, attacker: "Entity", amount: int) -> str:
"""
The entity takes damage from the attacker
based on their respective stats.
"""
self.health -= amount
damage = max(0, amount - self.constitution)
self.health -= damage
if self.health <= 0:
self.die()
return _("{name} takes {amount} damage.")\
.format(name=self.translated_name.capitalize(), amount=str(amount))\
return _("{name} takes {damage} damage.")\
.format(name=self.translated_name.capitalize(), damage=str(damage))\
+ (" " + _("{name} dies.")
.format(name=self.translated_name.capitalize())
if self.health <= 0 else "")
@ -757,10 +781,10 @@ class InventoryHolder(Entity):
"""
for i in range(len(inventory)):
if isinstance(inventory[i], dict):
inventory[i] = self.dict_to_inventory(inventory[i])
inventory[i] = self.dict_to_item(inventory[i])
return inventory
def dict_to_inventory(self, item_dict: dict) -> Entity:
def dict_to_item(self, item_dict: dict) -> Entity:
"""
Translates a dictionnary that contains the state of an item
into an item object.
@ -783,13 +807,15 @@ class InventoryHolder(Entity):
"""
Adds an object to the inventory.
"""
self.inventory.append(obj)
if obj not in self.inventory:
self.inventory.append(obj)
def remove_from_inventory(self, obj: Any) -> None:
"""
Removes an object from the inventory.
"""
self.inventory.remove(obj)
if obj in self.inventory:
self.inventory.remove(obj)
def change_hazel_balance(self, hz: int) -> None:
"""

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: 2021-01-06 15:19+0100\n"
"POT-Creation-Date: 2021-01-08 01:57+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"
@ -17,6 +17,10 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} nimmt {amount} Schadenspunkte."
#: squirrelbattle/display/menudisplay.py:160
msgid "INVENTORY"
msgstr "BESTAND"
@ -25,11 +29,32 @@ msgstr "BESTAND"
msgid "STALL"
msgstr "STAND"
#: squirrelbattle/display/statsdisplay.py:36
#: squirrelbattle/display/statsdisplay.py:23
#: squirrelbattle/tests/translations_test.py:60
msgid "player"
msgstr "Spieler"
#: squirrelbattle/display/statsdisplay.py:35
msgid "Inventory:"
msgstr "Bestand:"
#: squirrelbattle/display/statsdisplay.py:55
#: squirrelbattle/display/statsdisplay.py:52
msgid "Equipped main:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:56
msgid "Equipped secondary:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:61
msgid "Equipped chestplate:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:65
msgid "Equipped helmet:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:72
msgid "YOU ARE DEAD"
msgstr "SIE WURDEN GESTORBEN"
@ -49,11 +74,11 @@ msgstr "Die Sonne ist warm heute"
#. 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:151
#: squirrelbattle/entities/items.py:163
msgid "Bomb is exploding."
msgstr "Die Bombe explodiert."
#: squirrelbattle/entities/items.py:248
#: squirrelbattle/entities/items.py:344
#, python-brace-format
msgid "{player} exchanged its body with {entity}."
msgstr "{player} täuscht seinem Körper mit {entity} aus."
@ -96,6 +121,10 @@ msgstr ""
"Die JSON-Datei ist nicht korrekt.\n"
"Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht."
#: squirrelbattle/interfaces.py:452
msgid "It's a critical hit!"
msgstr ""
#: squirrelbattle/interfaces.py:453
#, python-brace-format
msgid "{name} hits {opponent}."
@ -103,8 +132,8 @@ msgstr "{name} schlägt {opponent}."
#: squirrelbattle/interfaces.py:465
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} nimmt {amount} Schadenspunkte."
msgid "{name} takes {damage} damage."
msgstr ""
#: squirrelbattle/interfaces.py:467
#, python-brace-format
@ -218,10 +247,6 @@ msgstr "Textur-Packung"
msgid "Language"
msgstr "Sprache"
#: squirrelbattle/tests/translations_test.py:62
msgid "player"
msgstr "Spieler"
#: squirrelbattle/tests/translations_test.py:64
msgid "hedgehog"
msgstr "Igel"

View File

@ -17,6 +17,10 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} recibe {amount} daño."
#: squirrelbattle/display/menudisplay.py:160
msgid "INVENTORY"
msgstr "INVENTORIO"
@ -25,11 +29,32 @@ msgstr "INVENTORIO"
msgid "STALL"
msgstr "PUESTO"
#: squirrelbattle/display/statsdisplay.py:36
#: squirrelbattle/display/statsdisplay.py:23
#: squirrelbattle/tests/translations_test.py:60
msgid "player"
msgstr "jugador"
#: squirrelbattle/display/statsdisplay.py:35
msgid "Inventory:"
msgstr "Inventorio :"
#: squirrelbattle/display/statsdisplay.py:55
#: squirrelbattle/display/statsdisplay.py:52
msgid "Equipped main:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:56
msgid "Equipped secondary:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:61
msgid "Equipped chestplate:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:65
msgid "Equipped helmet:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:72
msgid "YOU ARE DEAD"
msgstr "ERES MUERTO"
@ -48,11 +73,11 @@ msgstr "El sol está caliente hoy"
#. 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:151
#: squirrelbattle/entities/items.py:163
msgid "Bomb is exploding."
msgstr "La bomba está explotando."
#: squirrelbattle/entities/items.py:248
#: squirrelbattle/entities/items.py:344
#, python-brace-format
msgid "{player} exchanged its body with {entity}."
msgstr "{player} intercambió su cuerpo con {entity}."
@ -95,6 +120,10 @@ msgstr ""
"El JSON archivo no es correcto.\n"
"Su guarda parece corrupta. Fue eliminada."
#: squirrelbattle/interfaces.py:452
msgid "It's a critical hit!"
msgstr ""
#: squirrelbattle/interfaces.py:453
#, python-brace-format
msgid "{name} hits {opponent}."
@ -102,8 +131,8 @@ msgstr "{name} golpea a {opponent}."
#: squirrelbattle/interfaces.py:465
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} recibe {amount} daño."
msgid "{name} takes {damage} damage."
msgstr ""
#: squirrelbattle/interfaces.py:467
#, python-brace-format
@ -217,10 +246,6 @@ msgstr "Paquete de texturas"
msgid "Language"
msgstr "Languaje"
#: squirrelbattle/tests/translations_test.py:62
msgid "player"
msgstr "jugador"
#: squirrelbattle/tests/translations_test.py:64
msgid "hedgehog"
msgstr "erizo"

View File

@ -17,6 +17,10 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} prend {amount} points de dégât."
#: squirrelbattle/display/menudisplay.py:160
msgid "INVENTORY"
msgstr "INVENTAIRE"
@ -25,11 +29,32 @@ msgstr "INVENTAIRE"
msgid "STALL"
msgstr "STAND"
#: squirrelbattle/display/statsdisplay.py:36
#: squirrelbattle/display/statsdisplay.py:23
#: squirrelbattle/tests/translations_test.py:60
msgid "player"
msgstr "joueur"
#: squirrelbattle/display/statsdisplay.py:35
msgid "Inventory:"
msgstr "Inventaire :"
#: squirrelbattle/display/statsdisplay.py:55
#: squirrelbattle/display/statsdisplay.py:52
msgid "Equipped main:"
msgstr "Équipement principal :"
#: squirrelbattle/display/statsdisplay.py:56
msgid "Equipped secondary:"
msgstr "Équipement secondaire :"
#: squirrelbattle/display/statsdisplay.py:61
msgid "Equipped chestplate:"
msgstr "Plastron équipé :"
#: squirrelbattle/display/statsdisplay.py:65
msgid "Equipped helmet:"
msgstr "Casque équipé :"
#: squirrelbattle/display/statsdisplay.py:72
msgid "YOU ARE DEAD"
msgstr "VOUS ÊTES MORT"
@ -49,11 +74,11 @@ msgstr "Le soleil est chaud aujourd'hui"
#. 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:151
#: squirrelbattle/entities/items.py:163
msgid "Bomb is exploding."
msgstr "La bombe explose."
#: squirrelbattle/entities/items.py:248
#: squirrelbattle/entities/items.py:344
#, python-brace-format
msgid "{player} exchanged its body with {entity}."
msgstr "{player} a échangé son corps avec {entity}."
@ -96,6 +121,10 @@ msgstr ""
"Le fichier JSON de sauvegarde est incorrect.\n"
"Votre sauvegarde semble corrompue. Elle a été supprimée."
#: squirrelbattle/interfaces.py:452
msgid "It's a critical hit!"
msgstr "C'est un coup critique !"
#: squirrelbattle/interfaces.py:453
#, python-brace-format
msgid "{name} hits {opponent}."
@ -103,8 +132,8 @@ msgstr "{name} frappe {opponent}."
#: squirrelbattle/interfaces.py:465
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} prend {amount} points de dégât."
msgid "{name} takes {damage} damage."
msgstr "{name} prend {damage} dégâts."
#: squirrelbattle/interfaces.py:467
#, python-brace-format
@ -218,10 +247,6 @@ msgstr "Pack de textures"
msgid "Language"
msgstr "Langue"
#: squirrelbattle/tests/translations_test.py:62
msgid "player"
msgstr "joueur"
#: squirrelbattle/tests/translations_test.py:64
msgid "hedgehog"
msgstr "hérisson"
@ -256,7 +281,7 @@ msgstr "bombe"
#: squirrelbattle/tests/translations_test.py:73
msgid "explosion"
msgstr ""
msgstr "explosion"
#: squirrelbattle/tests/translations_test.py:74
msgid "heart"

View File

@ -19,6 +19,7 @@ class TestEntities(unittest.TestCase):
"""
self.map = Map.load(ResourceManager.get_asset_path("example_map.txt"))
self.player = Player()
self.player.constitution = 0
self.map.add_entity(self.player)
self.player.move(self.map.start_y, self.map.start_x)
@ -55,6 +56,7 @@ class TestEntities(unittest.TestCase):
self.assertTrue(entity.dead)
entity = Rabbit()
entity.critical = 0
self.map.add_entity(entity)
entity.move(15, 44)
# Move randomly
@ -76,6 +78,7 @@ class TestEntities(unittest.TestCase):
{self.player.name.capitalize()} takes {entity.strength} damage.")
# Fight the rabbit
self.player.critical = 0
old_health = entity.health
self.player.move_down()
self.assertEqual(entity.health, old_health - self.player.strength)

View File

@ -2,13 +2,16 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import random
import unittest
from ..bootstrap import Bootstrap
from ..display.display import Display
from ..display.display_manager import DisplayManager
from ..entities.friendly import Merchant, Sunflower
from ..entities.items import Bomb, Heart, Sword, Explosion
from ..entities.items import Bomb, Heart, Sword, Explosion, Shield, Helmet, \
Chestplate, RingCritical
from ..entities.monsters import GiantSeaEagle
from ..entities.player import Player
from ..enums import DisplayActions
from ..game import Game, KeyValues, GameMode
@ -613,6 +616,100 @@ class TestGame(unittest.TestCase):
self.game.handle_key_pressed(KeyValues.SPACE)
self.assertEqual(self.game.state, GameMode.PLAY)
def test_equipment(self) -> None:
"""
Ensure that equipment is working.
"""
self.game.state = GameMode.INVENTORY
# sword goes into the main equipment slot
sword = Sword()
sword.hold(self.game.player)
self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertEqual(self.game.player.equipped_main, sword)
self.assertFalse(self.game.player.inventory)
# shield goes into the secondary equipment slot
shield = Shield()
shield.hold(self.game.player)
self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertEqual(self.game.player.equipped_secondary, shield)
self.assertFalse(self.game.player.inventory)
# helmet goes into the helmet slot
helmet = Helmet()
helmet.hold(self.game.player)
self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertEqual(self.game.player.equipped_helmet, helmet)
self.assertFalse(self.game.player.inventory)
# helmet goes into the armor slot
chestplate = Chestplate()
chestplate.hold(self.game.player)
self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertEqual(self.game.player.equipped_armor, chestplate)
self.assertFalse(self.game.player.inventory)
# Use bomb
bomb = Bomb()
bomb.hold(self.game.player)
self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertEqual(self.game.player.equipped_secondary, bomb)
self.assertIn(shield, self.game.player.inventory)
self.game.state = GameMode.PLAY
self.game.handle_key_pressed(KeyValues.USE)
self.assertIsNone(self.game.player.equipped_secondary)
self.game.state = GameMode.INVENTORY
self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertEqual(self.game.player.equipped_secondary, shield)
self.assertFalse(self.game.player.inventory)
# Reequip, which is useless but covers code
sword.equip()
shield.equip()
helmet.equip()
chestplate.equip()
self.game.save_state()
# Unequip all
sword.unequip()
shield.unequip()
helmet.unequip()
chestplate.unequip()
self.assertIsNone(self.game.player.equipped_main)
self.assertIsNone(self.game.player.equipped_secondary)
self.assertIsNone(self.game.player.equipped_helmet)
self.assertIsNone(self.game.player.equipped_armor)
self.assertIn(sword, self.game.player.inventory)
self.assertIn(shield, self.game.player.inventory)
self.assertIn(helmet, self.game.player.inventory)
self.assertIn(chestplate, self.game.player.inventory)
# Test rings
self.game.player.inventory.clear()
ring = RingCritical()
ring.hold(self.game.player)
old_critical = self.game.player.critical
self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertEqual(self.game.player.critical,
old_critical + ring.critical)
self.game.save_state()
ring.unequip()
def test_critical_hit(self) -> None:
"""
Ensure that critical hits are working.
"""
random.seed(2) # Next random.randint(1, 100) will output 8
self.game.player.critical = 10
sea_eagle = GiantSeaEagle()
self.game.map.add_entity(sea_eagle)
sea_eagle.move(2, 6)
old_health = sea_eagle.health
self.game.player.hit(sea_eagle)
self.assertEqual(sea_eagle.health,
old_health - self.game.player.strength * 4)
def test_ladders(self) -> None:
"""
Ensure that the player can climb on ladders.