Merge branch 'master' into 'moreitems'

# Conflicts:
#   squirrelbattle/entities/items.py
#   squirrelbattle/interfaces.py
#   squirrelbattle/tests/game_test.py
This commit is contained in:
eichhornchen 2021-01-08 23:41:21 +01:00
commit eac9057f31
20 changed files with 503 additions and 180 deletions

View File

@ -57,7 +57,7 @@ Dans le `pack de textures`_ ASCII, il est représenté par le caractère ``8``.
Dans le `pack de textures`_ écureuil, il est représenté par l'émoji ``🧸``. Dans le `pack de textures`_ écureuil, il est représenté par l'émoji ``🧸``.
Pyguargue Pygargue
--------- ---------
Son nom est fixé à `eagle`. Il a par défaut une force à **1000** et **5000** points de vie. Son nom est fixé à `eagle`. Il a par défaut une force à **1000** et **5000** points de vie.

View File

@ -22,7 +22,7 @@ Pack de textures
.. _Bouclier: entities/items.html#bouclier .. _Bouclier: entities/items.html#bouclier
.. _Hazel: ../index.html .. _Hazel: ../index.html
.. _Plastron: ../entities/items.html#plastron .. _Plastron: ../entities/items.html#plastron
.. _Pyguargue: ../entities/monsters.html#Pyguargue .. _Pygargue: ../entities/monsters.html#Pygargue
.. _Casque: ../entities/items.html#Casque .. _Casque: ../entities/items.html#Casque
.. _Anneau: ../entities/items.html#Anneau .. _Anneau: ../entities/items.html#Anneau
.. _Trompette: ../entities/items.html#Trompette .. _Trompette: ../entities/items.html#Trompette
@ -64,7 +64,7 @@ Chaque tuile fait un caractère de large.
* Bouclier_ : ``D`` * Bouclier_ : ``D``
* Hazel_ : ``¤`` * Hazel_ : ``¤``
* Plastron_ : ``(`` * Plastron_ : ``(``
* Pyguargue_ : ``µ`` * Pygargue_ : ``µ``
* Casque_ : ``0`` * Casque_ : ``0``
* Anneau_ : ``o`` * Anneau_ : ``o``
* Trompette_ : ``/`` * Trompette_ : ``/``
@ -95,7 +95,7 @@ Chaque tuile fait 2 caractères de large pour afficher les émojis proprement.
* Bouclier_ : ``🛡️`` * Bouclier_ : ``🛡️``
* Hazel_ : ``🌰`` * Hazel_ : ``🌰``
* Plastron_ : ``🦺`` * Plastron_ : ``🦺``
* Pyguargue_ : ``🦅`` * Pygargue_ : ``🦅``
* Casque_ : ``⛑️`` * Casque_ : ``⛑️``
* Anneau_ : ``💍`` * Anneau_ : ``💍``
* Trompette_ : ``🎺`` * Trompette_ : ``🎺``

View File

@ -92,6 +92,6 @@ class CreditsDisplay(Display):
self.addstr(self.pad, y_offset + i, x_offset + j, c, self.addstr(self.pad, y_offset + i, x_offset + j, c,
fg_color, bg_color, bold=bold) fg_color, bg_color, bold=bold)
def handle_click(self, y: int, x: int, game: Game) -> None: def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
if self.pad.inch(y - 1, x - 1) != ord(" "): if self.pad.inch(y - 1, x - 1) != ord(" "):
self.ascii_art_displayed = True self.ascii_art_displayed = True

View File

@ -190,12 +190,11 @@ class Display:
""" """
raise NotImplementedError raise NotImplementedError
def handle_click(self, y: int, x: int, game: Game) -> None: def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
""" """
A mouse click was performed on the coordinates (y, x) of the pad. A mouse click was performed on the coordinates (y, x) of the pad.
Maybe it should do something. Maybe it should do something.
""" """
pass
@property @property
def rows(self) -> int: def rows(self) -> int:

View File

@ -66,7 +66,7 @@ class DisplayManager:
d.pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) d.pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK)
d.update(self.game) d.update(self.game)
def handle_mouse_click(self, y: int, x: int) -> None: def handle_mouse_click(self, y: int, x: int, attr: int) -> None:
""" """
Handles the mouse clicks. Handles the mouse clicks.
""" """
@ -79,7 +79,7 @@ class DisplayManager:
# of that display # of that display
display = d display = d
if display: if display:
display.handle_click(y - display.y, x - display.x, self.game) display.handle_click(y - display.y, x - display.x, attr, self.game)
def refresh(self) -> List[Display]: def refresh(self) -> List[Display]:
""" """

View File

@ -51,7 +51,7 @@ class MenuDisplay(Display):
self.height - 2 + self.y, self.height - 2 + self.y,
self.width - 2 + self.x) self.width - 2 + self.x)
def handle_click(self, y: int, x: int, game: Game) -> None: def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
""" """
We can select a menu item with the mouse. We can select a menu item with the mouse.
""" """
@ -135,13 +135,13 @@ class MainMenuDisplay(Display):
def update(self, game: Game) -> None: def update(self, game: Game) -> None:
self.menudisplay.update_menu(game.main_menu) self.menudisplay.update_menu(game.main_menu)
def handle_click(self, y: int, x: int, game: Game) -> None: def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
menuwidth = min(self.menudisplay.preferred_width, self.width) menuwidth = min(self.menudisplay.preferred_width, self.width)
menuy, menux = len(self.title) + 8, self.width // 2 - menuwidth // 2 - 1 menuy, menux = len(self.title) + 8, self.width // 2 - menuwidth // 2 - 1
menuheight = min(self.menudisplay.preferred_height, self.height - menuy) menuheight = min(self.menudisplay.preferred_height, self.height - menuy)
if menuy <= y < menuy + menuheight and menux <= x < menux + menuwidth: if menuy <= y < menuy + menuheight and menux <= x < menux + menuwidth:
self.menudisplay.handle_click(y - menuy, x - menux, game) self.menudisplay.handle_click(y - menuy, x - menux, attr, game)
if y <= len(self.title): if y <= len(self.title):
self.fg_color = randint(0, 1000), randint(0, 1000), randint(0, 1000) self.fg_color = randint(0, 1000), randint(0, 1000), randint(0, 1000)
@ -176,6 +176,7 @@ class PlayerInventoryDisplay(MenuDisplay):
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() + " " + item.translated_name.capitalize()
+ (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 ""))
@ -193,7 +194,7 @@ class PlayerInventoryDisplay(MenuDisplay):
def trueheight(self) -> int: def trueheight(self) -> int:
return 2 + super().trueheight return 2 + super().trueheight
def handle_click(self, y: int, x: int, game: Game) -> None: def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
""" """
We can select a menu item with the mouse. We can select a menu item with the mouse.
""" """
@ -221,6 +222,7 @@ class StoreInventoryDisplay(MenuDisplay):
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() + " " + item.translated_name.capitalize()
+ (f" ({item.description})" if item.description else "")
+ ": " + str(item.price) + " Hazels") + ": " + str(item.price) + " Hazels")
price = f"{self.pack.HAZELNUT} {self.menu.merchant.hazel} Hazels" price = f"{self.pack.HAZELNUT} {self.menu.merchant.hazel} Hazels"
@ -236,7 +238,7 @@ class StoreInventoryDisplay(MenuDisplay):
def trueheight(self) -> int: def trueheight(self) -> int:
return 2 + super().trueheight return 2 + super().trueheight
def handle_click(self, y: int, x: int, game: Game) -> None: def handle_click(self, y: int, x: int, attr: int, game: Game) -> None:
""" """
We can select a menu item with the mouse. We can select a menu item with the mouse.
""" """

View File

@ -3,8 +3,10 @@
import curses import curses
from ..entities.items import Monocle
from ..entities.player import Player from ..entities.player import Player
from ..game import Game from ..game import Game
from ..interfaces import FightingEntity
from ..translations import gettext as _ from ..translations import gettext as _
from .display import Display from .display import Display
@ -13,6 +15,7 @@ class StatsDisplay(Display):
""" """
A class to handle the display of the stats of the player. A class to handle the display of the stats of the player.
""" """
game: Game
player: Player player: Player
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -20,6 +23,7 @@ class StatsDisplay(Display):
self.pad = self.newpad(self.rows, self.cols) self.pad = self.newpad(self.rows, self.cols)
def update(self, game: Game) -> None: def update(self, game: Game) -> None:
self.game = game
self.player = game.player self.player = game.player
def update_pad(self) -> None: def update_pad(self) -> None:
@ -77,6 +81,47 @@ class StatsDisplay(Display):
self.addstr(self.pad, 15, 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) bold=True, blink=True, standout=True)
if self.player.map.tiles[self.player.y][self.player.x].is_ladder():
msg = _("Use {key} to use the ladder") \
.format(key=self.game.settings.KEY_LADDER.upper())
self.addstr(self.pad, self.height - 2, 0, msg,
italic=True, reverse=True)
self.update_entities_stats()
def update_entities_stats(self) -> None:
"""
Display information about a near entity if we have a monocle.
"""
for dy, dx in [(-1, 0), (0, -1), (0, 1), (1, 0)]:
for entity in self.player.map.find_entities(FightingEntity):
if entity == self.player:
continue
if entity.y == self.player.y + dy \
and entity.x == self.player.x + dx:
if entity.is_friendly():
msg = _("Move to the friendly entity to talk to it") \
if self.game.waiting_for_friendly_key else \
_("Use {key} then move to talk to the entity") \
.format(key=self.game.settings.KEY_CHAT.upper())
self.addstr(self.pad, self.height - 1, 0, msg,
italic=True, reverse=True)
if isinstance(self.player.equipped_secondary, Monocle):
# Truth monocle
message = f"{entity.translated_name.capitalize()} " \
f"{self.pack[entity.name.upper()]}\n" \
f"STR {entity.strength}\n" \
f"INT {entity.intelligence}\n" \
f"CHR {entity.charisma}\n" \
f"DEX {entity.dexterity}\n" \
f"CON {entity.constitution}\n" \
f"CRI {entity.critical}%"
self.addstr(self.pad, 17, 0, message)
# Only display one entity
break
def display(self) -> None: def display(self) -> None:
self.pad.erase() self.pad.erase()
self.update_pad() self.update_pad()

View File

@ -93,6 +93,7 @@ TexturePack.ASCII_PACK = TexturePack(
HEDGEHOG='*', HEDGEHOG='*',
HELMET='0', HELMET='0',
MERCHANT='M', MERCHANT='M',
MONOCLE='ô',
PLAYER='@', PLAYER='@',
RABBIT='Y', RABBIT='Y',
RING_OF_CRITICAL_DAMAGE='o', RING_OF_CRITICAL_DAMAGE='o',
@ -136,6 +137,7 @@ TexturePack.SQUIRREL_PACK = TexturePack(
HELMET='⛑️ ', HELMET='⛑️ ',
PLAYER='🐿️ ', PLAYER='🐿️ ',
MERCHANT='🦜', MERCHANT='🦜',
MONOCLE='🧐',
RABBIT='🐇', RABBIT='🐇',
RING_OF_CRITICAL_DAMAGE='💍', RING_OF_CRITICAL_DAMAGE='💍',
RING_OF_MORE_EXPERIENCE='💍', RING_OF_MORE_EXPERIENCE='💍',

View File

@ -24,6 +24,13 @@ class Item(Entity):
self.held_by = held_by self.held_by = held_by
self.price = price self.price = price
@property
def description(self) -> str:
"""
In the inventory, indicate the usefulness of the item.
"""
return ""
def drop(self) -> None: def drop(self) -> None:
""" """
The item is dropped from the inventory onto the floor. The item is dropped from the inventory onto the floor.
@ -84,9 +91,9 @@ class Item(Entity):
""" """
Returns the list of all item classes. Returns the list of all item classes.
""" """
return [BodySnatchPotion, Bomb, Heart, Shield, Sword, return [BodySnatchPotion, Bomb, Bow, Chestplate, FireBallStaff,
Chestplate, Helmet, RingCritical, RingXP, Heart, Helmet, Monocle, ScrollofDamage, ScrollofWeakening,
ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff] Shield, Sword, RingCritical, RingXP, Ruler]
def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder, def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder,
for_free: bool = False) -> bool: for_free: bool = False) -> bool:
@ -120,6 +127,10 @@ class Heart(Item):
super().__init__(name=name, price=price, *args, **kwargs) super().__init__(name=name, price=price, *args, **kwargs)
self.healing = healing self.healing = healing
@property
def description(self) -> str:
return f"HP+{self.healing}"
def hold(self, entity: InventoryHolder) -> None: def hold(self, entity: InventoryHolder) -> None:
""" """
When holding a heart, the player is healed and When holding a heart, the player is healed and
@ -228,6 +239,10 @@ class Weapon(Item):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.damage = damage self.damage = damage
@property
def description(self) -> str:
return f"STR+{self.damage}" if self.damage else super().description
def save_state(self) -> dict: def save_state(self) -> dict:
""" """
Saves the state of the weapon into a dictionary Saves the state of the weapon into a dictionary
@ -281,6 +296,11 @@ class Armor(Item):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.constitution = constitution self.constitution = constitution
@property
def description(self) -> str:
return f"CON+{self.constitution}" if self.constitution \
else super().description
def equip(self) -> None: def equip(self) -> None:
super().equip() super().equip()
self.held_by.constitution += self.constitution self.held_by.constitution += self.constitution
@ -398,6 +418,14 @@ class Ring(Item):
self.critical = critical self.critical = critical
self.experience = experience self.experience = experience
@property
def description(self) -> str:
fields = [("MAX HP", self.maxhealth), ("STR", self.strength),
("INT", self.intelligence), ("CHR", self.charisma),
("DEX", self.dexterity), ("CON", self.constitution),
("CRI", self.critical), ("XP", self.experience)]
return ", ".join(f"{key}+{value}" for key, value in fields if value)
def equip(self) -> None: def equip(self) -> None:
super().equip() super().equip()
self.held_by.maxhealth += self.maxhealth self.held_by.maxhealth += self.maxhealth
@ -446,7 +474,6 @@ class RingXP(Ring):
super().__init__(name=name, price=price, experience=experience, super().__init__(name=name, price=price, experience=experience,
*args, **kwargs) *args, **kwargs)
class ScrollofDamage(Item): class ScrollofDamage(Item):
""" """
A scroll that, when used, deals damage to all entities in a certain radius. A scroll that, when used, deals damage to all entities in a certain radius.
@ -604,3 +631,9 @@ class FireBallStaff(LongRangeWeapon):
explosion = Explosion(y=y, x=x) explosion = Explosion(y=y, x=x)
self.held_by.map.add_entity(explosion) self.held_by.map.add_entity(explosion)
class Monocle(Item):
def __init__(self, name: str = "monocle", price: int = 10,
*args, **kwargs):
super().__init__(name=name, price=price, *args, **kwargs)

View File

@ -43,7 +43,9 @@ class Monster(FightingEntity):
# that targets the player. # that targets the player.
# If they can not move and are already close to the player, # If they can not move and are already close to the player,
# they hit. # they hit.
if target and (self.y, self.x) in target.paths: if target and (self.y, self.x) in target.paths and \
self.map.is_visible_from(self.y, self.x,
target.y, target.x, 5):
# Moves to target player by choosing the best available path # Moves to target player by choosing the best available path
for next_y, next_x in target.paths[(self.y, self.x)]: for next_y, next_x in target.paths[(self.y, self.x)]:
moved = self.check_move(next_y, next_x, True) moved = self.check_move(next_y, next_x, True)

View File

@ -97,10 +97,14 @@ class Game:
screen.noutrefresh() screen.noutrefresh()
self.display_actions(DisplayActions.REFRESH) self.display_actions(DisplayActions.REFRESH)
curses.doupdate() curses.doupdate()
try:
key = screen.getkey() key = screen.getkey()
except KeyboardInterrupt:
exit(0)
return
if key == "KEY_MOUSE": if key == "KEY_MOUSE":
_ignored1, x, y, _ignored2, _ignored3 = curses.getmouse() _ignored1, x, y, _ignored2, attr = curses.getmouse()
self.display_actions(DisplayActions.MOUSE, y, x) self.display_actions(DisplayActions.MOUSE, y, x, attr)
else: else:
self.handle_key_pressed( self.handle_key_pressed(
KeyValues.translate_key(key, self.settings), key) KeyValues.translate_key(key, self.settings), key)
@ -401,14 +405,16 @@ class Game:
""" """
Saves the game to a dictionary. Saves the game to a dictionary.
""" """
return self.map.save_state() return dict(map_index=self.map_index,
maps=[m.save_state() for m in self.maps])
def load_state(self, d: dict) -> None: def load_state(self, d: dict) -> None:
""" """
Loads the game from a dictionary. Loads the game from a dictionary.
""" """
try: try:
self.map.load_state(d) self.map_index = d["map_index"]
self.maps = [Map().load_state(map_dict) for map_dict in d["maps"]]
except KeyError: except KeyError:
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.")
@ -425,6 +431,8 @@ class Game:
return return
self.player = players[0] self.player = players[0]
self.map.compute_visibility(self.player.y, self.player.x,
self.player.vision)
self.display_actions(DisplayActions.UPDATE) self.display_actions(DisplayActions.UPDATE)
def load_game(self) -> None: def load_game(self) -> None:

View File

@ -7,6 +7,7 @@ from random import choice, choices, randint
from typing import List, Optional, Any, Dict, Tuple from typing import List, Optional, Any, Dict, Tuple
from queue import PriorityQueue from queue import PriorityQueue
from functools import reduce from functools import reduce
from copy import deepcopy
from .display.texturepack import TexturePack from .display.texturepack import TexturePack
from .translations import gettext as _ from .translations import gettext as _
@ -80,18 +81,18 @@ class Map:
currentx: int currentx: int
currenty: int currenty: int
def __init__(self, width: int, height: int, tiles: list, def __init__(self, width: int = 0, height: int = 0, tiles: list = None,
start_y: int, start_x: int): start_y: int = 0, start_x: int = 0):
self.floor = 0 self.floor = 0
self.width = width self.width = width
self.height = height self.height = height
self.start_y = start_y self.start_y = start_y
self.start_x = start_x self.start_x = start_x
self.tiles = tiles self.tiles = tiles or []
self.visibility = [[False for _ in range(len(tiles[0]))] self.visibility = [[False for _ in range(len(self.tiles[0]))]
for _ in range(len(tiles))] for _ in range(len(self.tiles))]
self.seen_tiles = [[False for _ in range(len(tiles[0]))] self.seen_tiles = [[False for _ in range(len(tiles[0]))]
for _ in range(len(tiles))] for _ in range(len(self.tiles))]
self.entities = [] self.entities = []
self.logs = Logs() self.logs = Logs()
@ -193,6 +194,14 @@ class Map:
entity.move(y, x) entity.move(y, x)
self.add_entity(entity) self.add_entity(entity)
def is_visible_from(self, starty: int, startx: int, desty: int, destx: int,
max_range: int) -> bool:
oldvisibility = deepcopy(self.visibility)
self.compute_visibility(starty, startx, max_range)
result = self.visibility[desty][destx]
self.visibility = oldvisibility
return result
def compute_visibility(self, y: int, x: int, max_range: int) -> None: def compute_visibility(self, y: int, x: int, max_range: int) -> None:
""" """
Sets the visible tiles to be the ones visible by an entity at point Sets the visible tiles to be the ones visible by an entity at point
@ -245,10 +254,12 @@ class Map:
if x + y > max_range: if x + y > max_range:
continue continue
is_opaque = self.is_wall(y, x, octant, origin) is_opaque = self.is_wall(y, x, octant, origin)
if y == top_y and octant == 7 and x == 4:
self.logs.add_message(f"{x}, {y}, {top.X}, {top.Y}")
is_visible = is_opaque\ is_visible = is_opaque\
or ((y != top_y or top > Slope(y * 4 - 1, x * 4 + 1)) or ((y != top_y or top >= Slope(y, x))
and (y != bottom_y and (y != bottom_y
or bottom < Slope(y * 4 + 1, x * 4 - 1))) or bottom <= Slope(y, x)))
# is_visible = is_opaque\ # is_visible = is_opaque\
# or ((y != top_y or top >= Slope(y, x)) # or ((y != top_y or top >= Slope(y, x))
# and (y != bottom_y or bottom <= Slope(y, x))) # and (y != bottom_y or bottom <= Slope(y, x)))
@ -338,9 +349,10 @@ class Map:
for enti in self.entities: for enti in self.entities:
d["entities"].append(enti.save_state()) d["entities"].append(enti.save_state())
d["map"] = self.draw_string(TexturePack.ASCII_PACK) d["map"] = self.draw_string(TexturePack.ASCII_PACK)
d["seen_tiles"] = self.seen_tiles
return d return d
def load_state(self, d: dict) -> None: def load_state(self, d: dict) -> "Map":
""" """
Loads the map's attributes from a dictionary. Loads the map's attributes from a dictionary.
""" """
@ -351,11 +363,16 @@ class Map:
self.currentx = d["currentx"] self.currentx = d["currentx"]
self.currenty = d["currenty"] self.currenty = d["currenty"]
self.tiles = self.load_dungeon_from_string(d["map"]) self.tiles = self.load_dungeon_from_string(d["map"])
self.seen_tiles = d["seen_tiles"]
self.visibility = [[False for _ in range(len(self.tiles[0]))]
for _ in range(len(self.tiles))]
self.entities = [] self.entities = []
dictclasses = Entity.get_all_entity_classes_in_a_dict() dictclasses = Entity.get_all_entity_classes_in_a_dict()
for entisave in d["entities"]: for entisave in d["entities"]:
self.add_entity(dictclasses[entisave["type"]](**entisave)) self.add_entity(dictclasses[entisave["type"]](**entisave))
return self
class Tile(Enum): class Tile(Enum):
""" """
@ -636,32 +653,34 @@ class Entity:
Trumpet, Chest Trumpet, Chest
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \
Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \ Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \
ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff, \
Monocle
return { return {
"Tiger": Tiger,
"Bomb": Bomb,
"Heart": Heart,
"BodySnatchPotion": BodySnatchPotion, "BodySnatchPotion": BodySnatchPotion,
"Hedgehog": Hedgehog, "Bomb": Bomb,
"Rabbit": Rabbit, "Bow": Bow,
"TeddyBear": TeddyBear, "Chest": Chest,
"Player": Player,
"Merchant": Merchant,
"Sunflower": Sunflower,
"Sword": Sword,
"Trumpet": Trumpet,
"Eagle": GiantSeaEagle,
"Shield": Shield,
"Chestplate": Chestplate, "Chestplate": Chestplate,
"Eagle": GiantSeaEagle,
"FireBallStaff": FireBallStaff,
"Heart": Heart,
"Hedgehog": Hedgehog,
"Helmet": Helmet, "Helmet": Helmet,
"Merchant": Merchant,
"Monocle": Monocle,
"Player": Player,
"Rabbit": Rabbit,
"RingCritical": RingCritical, "RingCritical": RingCritical,
"RingXP": RingXP, "RingXP": RingXP,
"Ruler": Ruler, "Ruler": Ruler,
"ScrollofDamage": ScrollofDamage, "ScrollofDamage": ScrollofDamage,
"ScrollofWeakening": ScrollofWeakening, "ScrollofWeakening": ScrollofWeakening,
"Bow": Bow, "Shield": Shield,
"FireBallStaff": FireBallStaff, "Sunflower": Sunflower,
"Chest": Chest, "Sword": Sword,
"Trumpet": Trumpet,
"TeddyBear": TeddyBear,
"Tiger": Tiger,
} }
def save_state(self) -> dict: def save_state(self) -> dict:

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: squirrelbattle 3.14.1\n" "Project-Id-Version: squirrelbattle 3.14.1\n"
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
"POT-Creation-Date: 2021-01-08 01:57+0100\n" "POT-Creation-Date: 2021-01-08 15:15+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,87 +17,116 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "ring_of_critical_damage"
msgstr ""
msgid "ring_of_more_experience"
msgstr ""
#, python-brace-format #, python-brace-format
msgid "{name} takes {amount} damage." msgid "{name} takes {amount} damage."
msgstr "{name} nimmt {amount} Schadenspunkte." msgstr "{name} nimmt {amount} Schadenspunkte."
#: squirrelbattle/display/menudisplay.py:160 #: squirrelbattle/display/creditsdisplay.py:28
#: squirrelbattle/display/menudisplay.py:123
#: squirrelbattle/display/menudisplay.py:148
msgid "Credits"
msgstr ""
#: squirrelbattle/display/creditsdisplay.py:32
msgid "Developers:"
msgstr ""
#: squirrelbattle/display/creditsdisplay.py:38
msgid "Translators:"
msgstr ""
#: squirrelbattle/display/menudisplay.py:168
msgid "INVENTORY" msgid "INVENTORY"
msgstr "BESTAND" msgstr "BESTAND"
#: squirrelbattle/display/menudisplay.py:202 #: squirrelbattle/display/menudisplay.py:214
msgid "STALL" msgid "STALL"
msgstr "STAND" msgstr "STAND"
#: squirrelbattle/display/statsdisplay.py:23 #: squirrelbattle/display/statsdisplay.py:44
#: squirrelbattle/tests/translations_test.py:60
msgid "player"
msgstr "Spieler"
#: squirrelbattle/display/statsdisplay.py:35
msgid "Inventory:" msgid "Inventory:"
msgstr "Bestand:" msgstr "Bestand:"
#: squirrelbattle/display/statsdisplay.py:52 #: squirrelbattle/display/statsdisplay.py:61
msgid "Equipped main:" msgid "Equipped main:"
msgstr "" msgstr ""
#: squirrelbattle/display/statsdisplay.py:56 #: squirrelbattle/display/statsdisplay.py:65
msgid "Equipped secondary:" msgid "Equipped secondary:"
msgstr "" msgstr ""
#: squirrelbattle/display/statsdisplay.py:61 #: squirrelbattle/display/statsdisplay.py:70
msgid "Equipped chestplate:" msgid "Equipped chestplate:"
msgstr "" msgstr ""
#: squirrelbattle/display/statsdisplay.py:65 #: squirrelbattle/display/statsdisplay.py:74
msgid "Equipped helmet:" msgid "Equipped helmet:"
msgstr "" msgstr ""
#: squirrelbattle/display/statsdisplay.py:72 #: squirrelbattle/display/statsdisplay.py:81
msgid "YOU ARE DEAD" msgid "YOU ARE DEAD"
msgstr "SIE WURDEN GESTORBEN" msgstr "SIE WURDEN GESTORBEN"
#: squirrelbattle/display/statsdisplay.py:85
#, python-brace-format
msgid "Use {key} to use the ladder"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:94
msgid "Move to the friendly entity to talk to it"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:96
#, python-brace-format
msgid "Use {key} then move to talk to the entity"
msgstr ""
#. TODO #. TODO
#: squirrelbattle/entities/friendly.py:33 #: squirrelbattle/entities/friendly.py:33
msgid "I don't sell any squirrel" msgid "I don't sell any squirrel"
msgstr "Ich verkaufe keinen Eichhörnchen." msgstr "Ich verkaufe keinen Eichhörnchen."
#: squirrelbattle/entities/friendly.py:52 #: squirrelbattle/entities/friendly.py:55
msgid "Flower power!!" msgid "Flower power!!"
msgstr "Blumenmacht!!" msgstr "Blumenmacht!!"
#: squirrelbattle/entities/friendly.py:52 #: squirrelbattle/entities/friendly.py:55
msgid "The sun is warm today" msgid "The sun is warm today"
msgstr "Die Sonne ist warm heute" msgstr "Die Sonne ist warm heute"
#. The bomb is exploding. #. The bomb is exploding.
#. Each entity that is close to the bomb takes damages. #. Each entity that is close to the bomb takes damages.
#. The player earn XP if the entity was killed. #. The player earn XP if the entity was killed.
#: squirrelbattle/entities/items.py:163 #: squirrelbattle/entities/items.py:178
msgid "Bomb is exploding." msgid "Bomb is exploding."
msgstr "Die Bombe explodiert." msgstr "Die Bombe explodiert."
#: squirrelbattle/entities/items.py:344 #: squirrelbattle/entities/items.py:365
#, python-brace-format #, python-brace-format
msgid "{player} exchanged its body with {entity}." msgid "{player} exchanged its body with {entity}."
msgstr "{player} täuscht seinem Körper mit {entity} aus." msgstr "{player} täuscht seinem Körper mit {entity} aus."
#: squirrelbattle/game.py:182 #: squirrelbattle/game.py:200
#, python-brace-format #, python-brace-format
msgid "The player climbs down to the floor {floor}." msgid "The player climbs down to the floor {floor}."
msgstr "Der Spieler klettert auf dem Stock {floor} hinunter." msgstr "Der Spieler klettert auf dem Stock {floor} hinunter."
#: squirrelbattle/game.py:195 #: squirrelbattle/game.py:213
#, python-brace-format #, python-brace-format
msgid "The player climbs up the floor {floor}." msgid "The player climbs up the floor {floor}."
msgstr "Der Spieler klettert auf dem Stock {floor} hinoben." msgstr "Der Spieler klettert auf dem Stock {floor} hinoben."
#: squirrelbattle/game.py:285 squirrelbattle/tests/game_test.py:592 #: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603
msgid "The buyer does not have enough money" msgid "The buyer does not have enough money"
msgstr "Der Kaufer hat nicht genug Geld" msgstr "Der Kaufer hat nicht genug Geld"
#: squirrelbattle/game.py:328 #: squirrelbattle/game.py:349
msgid "" msgid ""
"Some keys are missing in your save file.\n" "Some keys are missing in your save file.\n"
"Your save seems to be corrupt. It got deleted." "Your save seems to be corrupt. It got deleted."
@ -105,7 +134,7 @@ msgstr ""
"In Ihrer Speicherdatei fehlen einige Schlüssel.\n" "In Ihrer Speicherdatei fehlen einige Schlüssel.\n"
"Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht." "Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht."
#: squirrelbattle/game.py:336 #: squirrelbattle/game.py:357
msgid "" msgid ""
"No player was found on this map!\n" "No player was found on this map!\n"
"Maybe you died?" "Maybe you died?"
@ -113,7 +142,7 @@ msgstr ""
"Auf dieser Karte wurde kein Spieler gefunden!\n" "Auf dieser Karte wurde kein Spieler gefunden!\n"
"Vielleicht sind Sie gestorben?" "Vielleicht sind Sie gestorben?"
#: squirrelbattle/game.py:356 #: squirrelbattle/game.py:379
msgid "" msgid ""
"The JSON file is not correct.\n" "The JSON file is not correct.\n"
"Your save seems corrupted. It got deleted." "Your save seems corrupted. It got deleted."
@ -121,26 +150,26 @@ msgstr ""
"Die JSON-Datei ist nicht korrekt.\n" "Die JSON-Datei ist nicht korrekt.\n"
"Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht." "Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht."
#: squirrelbattle/interfaces.py:452 #: squirrelbattle/interfaces.py:718
msgid "It's a critical hit!" msgid "It's a critical hit!"
msgstr "" msgstr ""
#: squirrelbattle/interfaces.py:453 #: squirrelbattle/interfaces.py:719
#, python-brace-format #, python-brace-format
msgid "{name} hits {opponent}." msgid "{name} hits {opponent}."
msgstr "{name} schlägt {opponent}." msgstr "{name} schlägt {opponent}."
#: squirrelbattle/interfaces.py:465 #: squirrelbattle/interfaces.py:733
#, python-brace-format #, python-brace-format
msgid "{name} takes {damage} damage." msgid "{name} takes {damage} damage."
msgstr "" msgstr ""
#: squirrelbattle/interfaces.py:467 #: squirrelbattle/interfaces.py:735
#, python-brace-format #, python-brace-format
msgid "{name} dies." msgid "{name} dies."
msgstr "{name} stirbt." msgstr "{name} stirbt."
#: squirrelbattle/interfaces.py:501 #: squirrelbattle/interfaces.py:769
#, python-brace-format #, python-brace-format
msgid "{entity} said: {message}" msgid "{entity} said: {message}"
msgstr "{entity} hat gesagt: {message}" msgstr "{entity} hat gesagt: {message}"
@ -149,8 +178,8 @@ msgstr "{entity} hat gesagt: {message}"
msgid "Back" msgid "Back"
msgstr "Zurück" msgstr "Zurück"
#: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 #: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371
#: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 #: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377
#: squirrelbattle/tests/translations_test.py:16 #: squirrelbattle/tests/translations_test.py:16
msgid "New game" msgid "New game"
msgstr "Neu Spiel" msgstr "Neu Spiel"
@ -247,6 +276,10 @@ msgstr "Textur-Packung"
msgid "Language" msgid "Language"
msgstr "Sprache" msgstr "Sprache"
#: squirrelbattle/tests/translations_test.py:62
msgid "player"
msgstr "Spieler"
#: squirrelbattle/tests/translations_test.py:64 #: squirrelbattle/tests/translations_test.py:64
msgid "hedgehog" msgid "hedgehog"
msgstr "Igel" msgstr "Igel"
@ -271,22 +304,50 @@ msgstr "Teddybär"
msgid "tiger" msgid "tiger"
msgstr "Tiger" msgstr "Tiger"
#: squirrelbattle/tests/translations_test.py:71 #: squirrelbattle/tests/translations_test.py:70
msgid "eagle"
msgstr ""
#: squirrelbattle/tests/translations_test.py:72
msgid "body snatch potion" msgid "body snatch potion"
msgstr "Leichenfleddererzaubertrank" msgstr "Leichenfleddererzaubertrank"
#: squirrelbattle/tests/translations_test.py:72 #: squirrelbattle/tests/translations_test.py:73
msgid "bomb" msgid "bomb"
msgstr "Bombe" msgstr "Bombe"
#: squirrelbattle/tests/translations_test.py:73 #: squirrelbattle/tests/translations_test.py:74
msgid "explosion" msgid "explosion"
msgstr "Explosion" msgstr "Explosion"
#: squirrelbattle/tests/translations_test.py:74 #: squirrelbattle/tests/translations_test.py:75
msgid "heart" msgid "heart"
msgstr "Herz" msgstr "Herz"
#: squirrelbattle/tests/translations_test.py:75 #: squirrelbattle/tests/translations_test.py:76
msgid "sword" msgid "sword"
msgstr "schwert" msgstr "schwert"
#: squirrelbattle/tests/translations_test.py:77
msgid "helmet"
msgstr ""
#: squirrelbattle/tests/translations_test.py:78
msgid "chestplate"
msgstr ""
#: squirrelbattle/tests/translations_test.py:79
msgid "shield"
msgstr ""
#: squirrelbattle/tests/translations_test.py:80
msgid "ring of critical damage"
msgstr ""
#: squirrelbattle/tests/translations_test.py:82
msgid "ring of more experience"
msgstr ""
#: squirrelbattle/tests/translations_test.py:84
msgid "monocle"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: squirrelbattle 3.14.1\n" "Project-Id-Version: squirrelbattle 3.14.1\n"
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
"POT-Creation-Date: 2021-01-06 15:19+0100\n" "POT-Creation-Date: 2021-01-08 15:15+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,86 +17,115 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
msgid "ring_of_critical_damage"
msgstr ""
msgid "ring_of_more_experience"
msgstr ""
#, python-brace-format #, python-brace-format
msgid "{name} takes {amount} damage." msgid "{name} takes {amount} damage."
msgstr "{name} recibe {amount} daño." msgstr "{name} recibe {amount} daño."
#: squirrelbattle/display/menudisplay.py:160 #: squirrelbattle/display/creditsdisplay.py:28
#: squirrelbattle/display/menudisplay.py:123
#: squirrelbattle/display/menudisplay.py:148
msgid "Credits"
msgstr ""
#: squirrelbattle/display/creditsdisplay.py:32
msgid "Developers:"
msgstr ""
#: squirrelbattle/display/creditsdisplay.py:38
msgid "Translators:"
msgstr ""
#: squirrelbattle/display/menudisplay.py:168
msgid "INVENTORY" msgid "INVENTORY"
msgstr "INVENTORIO" msgstr "INVENTORIO"
#: squirrelbattle/display/menudisplay.py:202 #: squirrelbattle/display/menudisplay.py:214
msgid "STALL" msgid "STALL"
msgstr "PUESTO" msgstr "PUESTO"
#: squirrelbattle/display/statsdisplay.py:23 #: squirrelbattle/display/statsdisplay.py:44
#: squirrelbattle/tests/translations_test.py:60
msgid "player"
msgstr "jugador"
#: squirrelbattle/display/statsdisplay.py:35
msgid "Inventory:" msgid "Inventory:"
msgstr "Inventorio :" msgstr "Inventorio :"
#: squirrelbattle/display/statsdisplay.py:52 #: squirrelbattle/display/statsdisplay.py:61
msgid "Equipped main:" msgid "Equipped main:"
msgstr "" msgstr ""
#: squirrelbattle/display/statsdisplay.py:56 #: squirrelbattle/display/statsdisplay.py:65
msgid "Equipped secondary:" msgid "Equipped secondary:"
msgstr "" msgstr ""
#: squirrelbattle/display/statsdisplay.py:61 #: squirrelbattle/display/statsdisplay.py:70
msgid "Equipped chestplate:" msgid "Equipped chestplate:"
msgstr "" msgstr ""
#: squirrelbattle/display/statsdisplay.py:65 #: squirrelbattle/display/statsdisplay.py:74
msgid "Equipped helmet:" msgid "Equipped helmet:"
msgstr "" msgstr ""
#: squirrelbattle/display/statsdisplay.py:72 #: squirrelbattle/display/statsdisplay.py:81
msgid "YOU ARE DEAD" msgid "YOU ARE DEAD"
msgstr "ERES MUERTO" msgstr "ERES MUERTO"
#: squirrelbattle/display/statsdisplay.py:85
#, python-brace-format
msgid "Use {key} to use the ladder"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:94
msgid "Move to the friendly entity to talk to it"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:96
#, python-brace-format
msgid "Use {key} then move to talk to the entity"
msgstr ""
#: squirrelbattle/entities/friendly.py:33 #: squirrelbattle/entities/friendly.py:33
msgid "I don't sell any squirrel" msgid "I don't sell any squirrel"
msgstr "No vendo ninguna ardilla" msgstr "No vendo ninguna ardilla"
#: squirrelbattle/entities/friendly.py:52 #: squirrelbattle/entities/friendly.py:55
msgid "Flower power!!" msgid "Flower power!!"
msgstr "Poder de las flores!!" msgstr "Poder de las flores!!"
#: squirrelbattle/entities/friendly.py:52 #: squirrelbattle/entities/friendly.py:55
msgid "The sun is warm today" msgid "The sun is warm today"
msgstr "El sol está caliente hoy" msgstr "El sol está caliente hoy"
#. The bomb is exploding. #. The bomb is exploding.
#. Each entity that is close to the bomb takes damages. #. Each entity that is close to the bomb takes damages.
#. The player earn XP if the entity was killed. #. The player earn XP if the entity was killed.
#: squirrelbattle/entities/items.py:163 #: squirrelbattle/entities/items.py:178
msgid "Bomb is exploding." msgid "Bomb is exploding."
msgstr "La bomba está explotando." msgstr "La bomba está explotando."
#: squirrelbattle/entities/items.py:344 #: squirrelbattle/entities/items.py:365
#, python-brace-format #, python-brace-format
msgid "{player} exchanged its body with {entity}." msgid "{player} exchanged its body with {entity}."
msgstr "{player} intercambió su cuerpo con {entity}." msgstr "{player} intercambió su cuerpo con {entity}."
#: squirrelbattle/game.py:182 #: squirrelbattle/game.py:200
#, python-brace-format #, python-brace-format
msgid "The player climbs down to the floor {floor}." msgid "The player climbs down to the floor {floor}."
msgstr "" msgstr ""
#: squirrelbattle/game.py:195 #: squirrelbattle/game.py:213
#, python-brace-format #, python-brace-format
msgid "The player climbs up the floor {floor}." msgid "The player climbs up the floor {floor}."
msgstr "" msgstr ""
#: squirrelbattle/game.py:285 squirrelbattle/tests/game_test.py:592 #: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603
msgid "The buyer does not have enough money" msgid "The buyer does not have enough money"
msgstr "El comprador no tiene suficiente dinero" msgstr "El comprador no tiene suficiente dinero"
#: squirrelbattle/game.py:328 #: squirrelbattle/game.py:349
msgid "" msgid ""
"Some keys are missing in your save file.\n" "Some keys are missing in your save file.\n"
"Your save seems to be corrupt. It got deleted." "Your save seems to be corrupt. It got deleted."
@ -104,7 +133,7 @@ msgstr ""
"Algunas claves faltan en su archivo de guarda.\n" "Algunas claves faltan en su archivo de guarda.\n"
"Su guarda parece a ser corruptido. Fue eliminado." "Su guarda parece a ser corruptido. Fue eliminado."
#: squirrelbattle/game.py:336 #: squirrelbattle/game.py:357
msgid "" msgid ""
"No player was found on this map!\n" "No player was found on this map!\n"
"Maybe you died?" "Maybe you died?"
@ -112,7 +141,7 @@ msgstr ""
"No jugador encontrado sobre la carta !\n" "No jugador encontrado sobre la carta !\n"
"¿ Quizas murió ?" "¿ Quizas murió ?"
#: squirrelbattle/game.py:356 #: squirrelbattle/game.py:379
msgid "" msgid ""
"The JSON file is not correct.\n" "The JSON file is not correct.\n"
"Your save seems corrupted. It got deleted." "Your save seems corrupted. It got deleted."
@ -120,26 +149,26 @@ msgstr ""
"El JSON archivo no es correcto.\n" "El JSON archivo no es correcto.\n"
"Su guarda parece corrupta. Fue eliminada." "Su guarda parece corrupta. Fue eliminada."
#: squirrelbattle/interfaces.py:452 #: squirrelbattle/interfaces.py:718
msgid "It's a critical hit!" msgid "It's a critical hit!"
msgstr "" msgstr ""
#: squirrelbattle/interfaces.py:453 #: squirrelbattle/interfaces.py:719
#, python-brace-format #, python-brace-format
msgid "{name} hits {opponent}." msgid "{name} hits {opponent}."
msgstr "{name} golpea a {opponent}." msgstr "{name} golpea a {opponent}."
#: squirrelbattle/interfaces.py:465 #: squirrelbattle/interfaces.py:733
#, python-brace-format #, python-brace-format
msgid "{name} takes {damage} damage." msgid "{name} takes {damage} damage."
msgstr "" msgstr ""
#: squirrelbattle/interfaces.py:467 #: squirrelbattle/interfaces.py:735
#, python-brace-format #, python-brace-format
msgid "{name} dies." msgid "{name} dies."
msgstr "{name} se muere." msgstr "{name} se muere."
#: squirrelbattle/interfaces.py:501 #: squirrelbattle/interfaces.py:769
#, python-brace-format #, python-brace-format
msgid "{entity} said: {message}" msgid "{entity} said: {message}"
msgstr "{entity} dijo : {message}" msgstr "{entity} dijo : {message}"
@ -148,8 +177,8 @@ msgstr "{entity} dijo : {message}"
msgid "Back" msgid "Back"
msgstr "Volver" msgstr "Volver"
#: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 #: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371
#: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 #: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377
#: squirrelbattle/tests/translations_test.py:16 #: squirrelbattle/tests/translations_test.py:16
msgid "New game" msgid "New game"
msgstr "Nuevo partido" msgstr "Nuevo partido"
@ -246,6 +275,10 @@ msgstr "Paquete de texturas"
msgid "Language" msgid "Language"
msgstr "Languaje" msgstr "Languaje"
#: squirrelbattle/tests/translations_test.py:62
msgid "player"
msgstr "jugador"
#: squirrelbattle/tests/translations_test.py:64 #: squirrelbattle/tests/translations_test.py:64
msgid "hedgehog" msgid "hedgehog"
msgstr "erizo" msgstr "erizo"
@ -270,22 +303,50 @@ msgstr "osito de peluche"
msgid "tiger" msgid "tiger"
msgstr "tigre" msgstr "tigre"
#: squirrelbattle/tests/translations_test.py:71 #: squirrelbattle/tests/translations_test.py:70
msgid "eagle"
msgstr ""
#: squirrelbattle/tests/translations_test.py:72
msgid "body snatch potion" msgid "body snatch potion"
msgstr "poción de intercambio" msgstr "poción de intercambio"
#: squirrelbattle/tests/translations_test.py:72 #: squirrelbattle/tests/translations_test.py:73
msgid "bomb" msgid "bomb"
msgstr "bomba" msgstr "bomba"
#: squirrelbattle/tests/translations_test.py:73 #: squirrelbattle/tests/translations_test.py:74
msgid "explosion" msgid "explosion"
msgstr "explosión" msgstr "explosión"
#: squirrelbattle/tests/translations_test.py:74 #: squirrelbattle/tests/translations_test.py:75
msgid "heart" msgid "heart"
msgstr "corazón" msgstr "corazón"
#: squirrelbattle/tests/translations_test.py:75 #: squirrelbattle/tests/translations_test.py:76
msgid "sword" msgid "sword"
msgstr "espada" msgstr "espada"
#: squirrelbattle/tests/translations_test.py:77
msgid "helmet"
msgstr ""
#: squirrelbattle/tests/translations_test.py:78
msgid "chestplate"
msgstr ""
#: squirrelbattle/tests/translations_test.py:79
msgid "shield"
msgstr ""
#: squirrelbattle/tests/translations_test.py:80
msgid "ring of critical damage"
msgstr ""
#: squirrelbattle/tests/translations_test.py:82
msgid "ring of more experience"
msgstr ""
#: squirrelbattle/tests/translations_test.py:84
msgid "monocle"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: squirrelbattle 3.14.1\n" "Project-Id-Version: squirrelbattle 3.14.1\n"
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
"POT-Creation-Date: 2021-01-06 15:19+0100\n" "POT-Creation-Date: 2021-01-08 15:15+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -21,83 +21,106 @@ msgstr ""
msgid "{name} takes {amount} damage." msgid "{name} takes {amount} damage."
msgstr "{name} prend {amount} points de dégât." msgstr "{name} prend {amount} points de dégât."
#: squirrelbattle/display/menudisplay.py:160 #: squirrelbattle/display/creditsdisplay.py:28
#: squirrelbattle/display/menudisplay.py:123
#: squirrelbattle/display/menudisplay.py:148
msgid "Credits"
msgstr ""
#: squirrelbattle/display/creditsdisplay.py:32
msgid "Developers:"
msgstr ""
#: squirrelbattle/display/creditsdisplay.py:38
msgid "Translators:"
msgstr ""
#: squirrelbattle/display/menudisplay.py:168
msgid "INVENTORY" msgid "INVENTORY"
msgstr "INVENTAIRE" msgstr "INVENTAIRE"
#: squirrelbattle/display/menudisplay.py:202 #: squirrelbattle/display/menudisplay.py:214
msgid "STALL" msgid "STALL"
msgstr "STAND" msgstr "STAND"
#: squirrelbattle/display/statsdisplay.py:23 #: squirrelbattle/display/statsdisplay.py:44
#: squirrelbattle/tests/translations_test.py:60
msgid "player"
msgstr "joueur"
#: squirrelbattle/display/statsdisplay.py:35
msgid "Inventory:" msgid "Inventory:"
msgstr "Inventaire :" msgstr "Inventaire :"
#: squirrelbattle/display/statsdisplay.py:52 #: squirrelbattle/display/statsdisplay.py:61
msgid "Equipped main:" msgid "Equipped main:"
msgstr "Équipement principal :" msgstr "Équipement principal :"
#: squirrelbattle/display/statsdisplay.py:56 #: squirrelbattle/display/statsdisplay.py:65
msgid "Equipped secondary:" msgid "Equipped secondary:"
msgstr "Équipement secondaire :" msgstr "Équipement secondaire :"
#: squirrelbattle/display/statsdisplay.py:61 #: squirrelbattle/display/statsdisplay.py:70
msgid "Equipped chestplate:" msgid "Equipped chestplate:"
msgstr "Plastron équipé :" msgstr "Plastron équipé :"
#: squirrelbattle/display/statsdisplay.py:65 #: squirrelbattle/display/statsdisplay.py:74
msgid "Equipped helmet:" msgid "Equipped helmet:"
msgstr "Casque équipé :" msgstr "Casque équipé :"
#: squirrelbattle/display/statsdisplay.py:72 #: squirrelbattle/display/statsdisplay.py:81
msgid "YOU ARE DEAD" msgid "YOU ARE DEAD"
msgstr "VOUS ÊTES MORT" msgstr "VOUS ÊTES MORT"
#: squirrelbattle/display/statsdisplay.py:85
#, python-brace-format
msgid "Use {key} to use the ladder"
msgstr "Appuyez sur {key} pour utiliser l'échelle"
#: squirrelbattle/display/statsdisplay.py:94
msgid "Move to the friendly entity to talk to it"
msgstr "Avancez vers l'entité pour lui parler"
#: squirrelbattle/display/statsdisplay.py:96
#, python-brace-format
msgid "Use {key} then move to talk to the entity"
msgstr "Appuyez sur {key} puis déplacez-vous pour parler"
#. TODO #. TODO
#: squirrelbattle/entities/friendly.py:33 #: squirrelbattle/entities/friendly.py:33
msgid "I don't sell any squirrel" msgid "I don't sell any squirrel"
msgstr "Je ne vends pas d'écureuil" msgstr "Je ne vends pas d'écureuil"
#: squirrelbattle/entities/friendly.py:52 #: squirrelbattle/entities/friendly.py:55
msgid "Flower power!!" msgid "Flower power!!"
msgstr "Pouvoir des fleurs !!" msgstr "Pouvoir des fleurs !!"
#: squirrelbattle/entities/friendly.py:52 #: squirrelbattle/entities/friendly.py:55
msgid "The sun is warm today" msgid "The sun is warm today"
msgstr "Le soleil est chaud aujourd'hui" msgstr "Le soleil est chaud aujourd'hui"
#. The bomb is exploding. #. The bomb is exploding.
#. Each entity that is close to the bomb takes damages. #. Each entity that is close to the bomb takes damages.
#. The player earn XP if the entity was killed. #. The player earn XP if the entity was killed.
#: squirrelbattle/entities/items.py:163 #: squirrelbattle/entities/items.py:178
msgid "Bomb is exploding." msgid "Bomb is exploding."
msgstr "La bombe explose." msgstr "La bombe explose."
#: squirrelbattle/entities/items.py:344 #: squirrelbattle/entities/items.py:365
#, python-brace-format #, python-brace-format
msgid "{player} exchanged its body with {entity}." msgid "{player} exchanged its body with {entity}."
msgstr "{player} a échangé son corps avec {entity}." msgstr "{player} a échangé son corps avec {entity}."
#: squirrelbattle/game.py:182 #: squirrelbattle/game.py:200
#, python-brace-format #, python-brace-format
msgid "The player climbs down to the floor {floor}." msgid "The player climbs down to the floor {floor}."
msgstr "Le joueur descend à l'étage {floor}." msgstr "Le joueur descend à l'étage {floor}."
#: squirrelbattle/game.py:195 #: squirrelbattle/game.py:213
#, python-brace-format #, python-brace-format
msgid "The player climbs up the floor {floor}." msgid "The player climbs up the floor {floor}."
msgstr "Le joueur monte à l'étage {floor}." msgstr "Le joueur monte à l'étage {floor}."
#: squirrelbattle/game.py:285 squirrelbattle/tests/game_test.py:592 #: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603
msgid "The buyer does not have enough money" msgid "The buyer does not have enough money"
msgstr "L'acheteur n'a pas assez d'argent" msgstr "L'acheteur n'a pas assez d'argent"
#: squirrelbattle/game.py:328 #: squirrelbattle/game.py:349
msgid "" msgid ""
"Some keys are missing in your save file.\n" "Some keys are missing in your save file.\n"
"Your save seems to be corrupt. It got deleted." "Your save seems to be corrupt. It got deleted."
@ -105,7 +128,7 @@ msgstr ""
"Certaines clés de votre ficher de sauvegarde sont manquantes.\n" "Certaines clés de votre ficher de sauvegarde sont manquantes.\n"
"Votre sauvegarde semble corrompue. Elle a été supprimée." "Votre sauvegarde semble corrompue. Elle a été supprimée."
#: squirrelbattle/game.py:336 #: squirrelbattle/game.py:357
msgid "" msgid ""
"No player was found on this map!\n" "No player was found on this map!\n"
"Maybe you died?" "Maybe you died?"
@ -113,7 +136,7 @@ msgstr ""
"Aucun joueur n'a été trouvé sur la carte !\n" "Aucun joueur n'a été trouvé sur la carte !\n"
"Peut-être êtes-vous mort ?" "Peut-être êtes-vous mort ?"
#: squirrelbattle/game.py:356 #: squirrelbattle/game.py:379
msgid "" msgid ""
"The JSON file is not correct.\n" "The JSON file is not correct.\n"
"Your save seems corrupted. It got deleted." "Your save seems corrupted. It got deleted."
@ -121,26 +144,26 @@ msgstr ""
"Le fichier JSON de sauvegarde est incorrect.\n" "Le fichier JSON de sauvegarde est incorrect.\n"
"Votre sauvegarde semble corrompue. Elle a été supprimée." "Votre sauvegarde semble corrompue. Elle a été supprimée."
#: squirrelbattle/interfaces.py:452 #: squirrelbattle/interfaces.py:718
msgid "It's a critical hit!" msgid "It's a critical hit!"
msgstr "C'est un coup critique !" msgstr "C'est un coup critique !"
#: squirrelbattle/interfaces.py:453 #: squirrelbattle/interfaces.py:719
#, python-brace-format #, python-brace-format
msgid "{name} hits {opponent}." msgid "{name} hits {opponent}."
msgstr "{name} frappe {opponent}." msgstr "{name} frappe {opponent}."
#: squirrelbattle/interfaces.py:465 #: squirrelbattle/interfaces.py:733
#, python-brace-format #, python-brace-format
msgid "{name} takes {damage} damage." msgid "{name} takes {damage} damage."
msgstr "{name} prend {damage} dégâts." msgstr "{name} prend {damage} dégâts."
#: squirrelbattle/interfaces.py:467 #: squirrelbattle/interfaces.py:735
#, python-brace-format #, python-brace-format
msgid "{name} dies." msgid "{name} dies."
msgstr "{name} meurt." msgstr "{name} meurt."
#: squirrelbattle/interfaces.py:501 #: squirrelbattle/interfaces.py:769
#, python-brace-format #, python-brace-format
msgid "{entity} said: {message}" msgid "{entity} said: {message}"
msgstr "{entity} a dit : {message}" msgstr "{entity} a dit : {message}"
@ -149,8 +172,8 @@ msgstr "{entity} a dit : {message}"
msgid "Back" msgid "Back"
msgstr "Retour" msgstr "Retour"
#: squirrelbattle/tests/game_test.py:358 squirrelbattle/tests/game_test.py:361 #: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371
#: squirrelbattle/tests/game_test.py:364 squirrelbattle/tests/game_test.py:367 #: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377
#: squirrelbattle/tests/translations_test.py:16 #: squirrelbattle/tests/translations_test.py:16
msgid "New game" msgid "New game"
msgstr "Nouvelle partie" msgstr "Nouvelle partie"
@ -247,6 +270,10 @@ msgstr "Pack de textures"
msgid "Language" msgid "Language"
msgstr "Langue" msgstr "Langue"
#: squirrelbattle/tests/translations_test.py:62
msgid "player"
msgstr "joueur"
#: squirrelbattle/tests/translations_test.py:64 #: squirrelbattle/tests/translations_test.py:64
msgid "hedgehog" msgid "hedgehog"
msgstr "hérisson" msgstr "hérisson"
@ -271,22 +298,50 @@ msgstr "nounours"
msgid "tiger" msgid "tiger"
msgstr "tigre" msgstr "tigre"
#: squirrelbattle/tests/translations_test.py:71 #: squirrelbattle/tests/translations_test.py:70
msgid "eagle"
msgstr "pygargue"
#: squirrelbattle/tests/translations_test.py:72
msgid "body snatch potion" msgid "body snatch potion"
msgstr "potion d'arrachage de corps" msgstr "potion d'arrachage de corps"
#: squirrelbattle/tests/translations_test.py:72 #: squirrelbattle/tests/translations_test.py:73
msgid "bomb" msgid "bomb"
msgstr "bombe" msgstr "bombe"
#: squirrelbattle/tests/translations_test.py:73 #: squirrelbattle/tests/translations_test.py:74
msgid "explosion" msgid "explosion"
msgstr "explosion" msgstr "explosion"
#: squirrelbattle/tests/translations_test.py:74 #: squirrelbattle/tests/translations_test.py:75
msgid "heart" msgid "heart"
msgstr "cœur" msgstr "cœur"
#: squirrelbattle/tests/translations_test.py:75 #: squirrelbattle/tests/translations_test.py:76
msgid "sword" msgid "sword"
msgstr "épée" msgstr "épée"
#: squirrelbattle/tests/translations_test.py:77
msgid "helmet"
msgstr "casque"
#: squirrelbattle/tests/translations_test.py:78
msgid "chestplate"
msgstr "plastron"
#: squirrelbattle/tests/translations_test.py:79
msgid "shield"
msgstr "bouclier"
#: squirrelbattle/tests/translations_test.py:80
msgid "ring of critical damage"
msgstr "anneau de coup critique"
#: squirrelbattle/tests/translations_test.py:82
msgid "ring of more experience"
msgstr "anneau de plus d'expérience"
#: squirrelbattle/tests/translations_test.py:84
msgid "monocle"
msgstr "monocle"

View File

@ -74,6 +74,7 @@ class Settings:
""" """
d = json.loads(json_str) d = json.loads(json_str)
for key in d: for key in d:
if hasattr(self, key):
setattr(self, key, d[key]) setattr(self, key, d[key])
def dumps_to_string(self) -> str: def dumps_to_string(self) -> str:

View File

@ -21,7 +21,7 @@ class TermManager: # pragma: no cover
# make cursor invisible # make cursor invisible
curses.curs_set(False) curses.curs_set(False)
# Catch mouse events # Catch mouse events
curses.mousemask(True) curses.mousemask(curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION)
# Enable colors # Enable colors
curses.start_color() curses.start_color()

View File

@ -1,11 +1,12 @@
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse # Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import random
import unittest import unittest
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart, Item, \ from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart, Item, \
Explosion Explosion
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, TeddyBear from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit,\
TeddyBear, GiantSeaEagle
from squirrelbattle.entities.friendly import Trumpet from squirrelbattle.entities.friendly import Trumpet
from squirrelbattle.entities.player import Player from squirrelbattle.entities.player import Player
from squirrelbattle.interfaces import Entity, Map from squirrelbattle.interfaces import Entity, Map
@ -264,3 +265,17 @@ class TestEntities(unittest.TestCase):
player_state = player.save_state() player_state = player.save_state()
self.assertEqual(player_state["current_xp"], 10) self.assertEqual(player_state["current_xp"], 10)
def test_critical_hit(self) -> None:
"""
Ensure that critical hits are working.
"""
random.seed(2) # Next random.randint(1, 100) will output 8
self.player.critical = 10
sea_eagle = GiantSeaEagle()
self.map.add_entity(sea_eagle)
sea_eagle.move(2, 6)
old_health = sea_eagle.health
self.player.hit(sea_eagle)
self.assertEqual(sea_eagle.health,
old_health - self.player.strength * 4)

View File

@ -1,8 +1,8 @@
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse # Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import curses
import os import os
import random
import unittest import unittest
from ..bootstrap import Bootstrap from ..bootstrap import Bootstrap
@ -11,7 +11,7 @@ from ..display.display_manager import DisplayManager
from ..entities.friendly import Merchant, Sunflower, Chest from ..entities.friendly import Merchant, Sunflower, Chest
from ..entities.items import Bomb, Heart, Sword, Explosion, Shield, Helmet, \ from ..entities.items import Bomb, Heart, Sword, Explosion, Shield, Helmet, \
Chestplate, RingCritical, Bow, FireBallStaff, ScrollofDamage,\ Chestplate, RingCritical, Bow, FireBallStaff, ScrollofDamage,\
ScrollofWeakening ScrollofWeakening, Monocle
from ..entities.monsters import Rabbit, GiantSeaEagle from ..entities.monsters import Rabbit, GiantSeaEagle
from ..entities.player import Player from ..entities.player import Player
from ..enums import DisplayActions, KeyValues, GameMode from ..enums import DisplayActions, KeyValues, GameMode
@ -67,6 +67,7 @@ class TestGame(unittest.TestCase):
new_state = self.game.save_state() new_state = self.game.save_state()
self.assertEqual(old_state, new_state) self.assertEqual(old_state, new_state)
self.assertIsNone(self.game.message)
# Ensure that the bomb is loaded # Ensure that the bomb is loaded
self.assertTrue(self.game.player.inventory) self.assertTrue(self.game.player.inventory)
@ -258,10 +259,12 @@ class TestGame(unittest.TestCase):
self.game.state = GameMode.MAINMENU self.game.state = GameMode.MAINMENU
# Change the color of the artwork # Change the color of the artwork
self.game.display_actions(DisplayActions.MOUSE, 0, 10) self.game.display_actions(DisplayActions.MOUSE, 0, 10,
curses.BUTTON1_CLICKED)
# Settings menu # Settings menu
self.game.display_actions(DisplayActions.MOUSE, 25, 21) self.game.display_actions(DisplayActions.MOUSE, 25, 21,
curses.BUTTON1_CLICKED)
self.assertEqual(self.game.main_menu.position, 4) self.assertEqual(self.game.main_menu.position, 4)
self.assertEqual(self.game.state, GameMode.SETTINGS) self.assertEqual(self.game.state, GameMode.SETTINGS)
@ -273,11 +276,13 @@ class TestGame(unittest.TestCase):
self.game.state = GameMode.INVENTORY self.game.state = GameMode.INVENTORY
# Click nowhere # Click nowhere
self.game.display_actions(DisplayActions.MOUSE, 0, 0) self.game.display_actions(DisplayActions.MOUSE, 0, 0,
curses.BUTTON1_CLICKED)
self.assertEqual(self.game.state, GameMode.INVENTORY) self.assertEqual(self.game.state, GameMode.INVENTORY)
# Click on the second item # Click on the second item
self.game.display_actions(DisplayActions.MOUSE, 8, 25) self.game.display_actions(DisplayActions.MOUSE, 8, 25,
curses.BUTTON1_CLICKED)
self.assertEqual(self.game.state, GameMode.INVENTORY) self.assertEqual(self.game.state, GameMode.INVENTORY)
self.assertEqual(self.game.inventory_menu.position, 1) self.assertEqual(self.game.inventory_menu.position, 1)
@ -573,12 +578,14 @@ class TestGame(unittest.TestCase):
# Buy the second item by clicking on it # Buy the second item by clicking on it
item = self.game.store_menu.validate() item = self.game.store_menu.validate()
self.assertIn(item, merchant.inventory) self.assertIn(item, merchant.inventory)
self.game.display_actions(DisplayActions.MOUSE, 7, 25) self.game.display_actions(DisplayActions.MOUSE, 7, 25,
curses.BUTTON1_CLICKED)
self.assertIn(item, self.game.player.inventory) self.assertIn(item, self.game.player.inventory)
self.assertNotIn(item, merchant.inventory) self.assertNotIn(item, merchant.inventory)
# Buy a heart # Buy a heart
merchant.inventory[1] = Heart() merchant.inventory[1] = Heart()
self.game.display_actions(DisplayActions.REFRESH)
item = self.game.store_menu.validate() item = self.game.store_menu.validate()
self.assertIn(item, merchant.inventory) self.assertIn(item, merchant.inventory)
self.assertEqual(item, merchant.inventory[1]) self.assertEqual(item, merchant.inventory[1])
@ -697,19 +704,21 @@ class TestGame(unittest.TestCase):
self.game.save_state() self.game.save_state()
ring.unequip() ring.unequip()
def test_critical_hit(self) -> None: def test_monocle(self) -> None:
""" """
Ensure that critical hits are working. The player is wearing a monocle, then the stats are displayed.
""" """
random.seed(2) # Next random.randint(1, 100) will output 8 self.game.state = GameMode.PLAY
self.game.player.critical = 10
monocle = Monocle()
monocle.hold(self.game.player)
monocle.equip()
sea_eagle = GiantSeaEagle() sea_eagle = GiantSeaEagle()
self.game.map.add_entity(sea_eagle) self.game.map.add_entity(sea_eagle)
sea_eagle.move(2, 6) sea_eagle.move(2, 6)
old_health = sea_eagle.health
self.game.player.hit(sea_eagle) self.game.display_actions(DisplayActions.REFRESH)
self.assertEqual(sea_eagle.health,
old_health - self.game.player.strength * 4)
def test_ladders(self) -> None: def test_ladders(self) -> None:
""" """
@ -748,9 +757,11 @@ class TestGame(unittest.TestCase):
""" """
self.game.state = GameMode.MAINMENU self.game.state = GameMode.MAINMENU
self.game.display_actions(DisplayActions.MOUSE, 41, 41) self.game.display_actions(DisplayActions.MOUSE, 41, 41,
curses.BUTTON1_CLICKED)
self.assertEqual(self.game.state, GameMode.CREDITS) self.assertEqual(self.game.state, GameMode.CREDITS)
self.game.display_actions(DisplayActions.MOUSE, 21, 21) self.game.display_actions(DisplayActions.MOUSE, 21, 21,
curses.BUTTON1_CLICKED)
self.game.display_actions(DisplayActions.REFRESH) self.game.display_actions(DisplayActions.REFRESH)
self.game.state = GameMode.CREDITS self.game.state = GameMode.CREDITS

View File

@ -67,9 +67,18 @@ class TestTranslations(unittest.TestCase):
self.assertEqual(_("sunflower"), "tournesol") self.assertEqual(_("sunflower"), "tournesol")
self.assertEqual(_("teddy bear"), "nounours") self.assertEqual(_("teddy bear"), "nounours")
self.assertEqual(_("tiger"), "tigre") self.assertEqual(_("tiger"), "tigre")
self.assertEqual(_("eagle"), "pygargue")
self.assertEqual(_("body snatch potion"), "potion d'arrachage de corps") self.assertEqual(_("body snatch potion"), "potion d'arrachage de corps")
self.assertEqual(_("bomb"), "bombe") self.assertEqual(_("bomb"), "bombe")
self.assertEqual(_("explosion"), "explosion") self.assertEqual(_("explosion"), "explosion")
self.assertEqual(_("heart"), "cœur") self.assertEqual(_("heart"), "cœur")
self.assertEqual(_("sword"), "épée") self.assertEqual(_("sword"), "épée")
self.assertEqual(_("helmet"), "casque")
self.assertEqual(_("chestplate"), "plastron")
self.assertEqual(_("shield"), "bouclier")
self.assertEqual(_("ring of critical damage"),
"anneau de coup critique")
self.assertEqual(_("ring of more experience"),
"anneau de plus d'expérience")
self.assertEqual(_("monocle"), "monocle")