diff --git a/setup.py b/setup.py index f051bbb..573eea7 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ with open("README.md", "r") as f: long_description = f.read() # Compile messages -for language in ["de", "en", "fr"]: +for language in ["de", "fr"]: args = ["msgfmt", "--check-format", "-o", f"squirrelbattle/locale/{language}/LC_MESSAGES" "/squirrelbattle.mo", diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index f7a0882..0e9cf04 100644 --- a/squirrelbattle/display/display_manager.py +++ b/squirrelbattle/display/display_manager.py @@ -6,8 +6,8 @@ from squirrelbattle.display.display import VerticalSplit, HorizontalSplit from squirrelbattle.display.mapdisplay import MapDisplay from squirrelbattle.display.messagedisplay import MessageDisplay from squirrelbattle.display.statsdisplay import StatsDisplay -from squirrelbattle.display.menudisplay import SettingsMenuDisplay, \ - MainMenuDisplay +from squirrelbattle.display.menudisplay import MainMenuDisplay, \ + InventoryDisplay, SettingsMenuDisplay from squirrelbattle.display.logsdisplay import LogsDisplay from squirrelbattle.display.texturepack import TexturePack from typing import Any @@ -23,10 +23,11 @@ class DisplayManager: pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) self.mapdisplay = MapDisplay(screen, pack) self.statsdisplay = StatsDisplay(screen, pack) + self.logsdisplay = LogsDisplay(screen, pack) + self.inventorydisplay = InventoryDisplay(screen, pack) self.mainmenudisplay = MainMenuDisplay(self.game.main_menu, screen, pack) self.settingsmenudisplay = SettingsMenuDisplay(screen, pack) - self.logsdisplay = LogsDisplay(screen, pack) self.messagedisplay = MessageDisplay(screen=screen, pack=None) self.hbar = HorizontalSplit(screen, pack) self.vbar = VerticalSplit(screen, pack) @@ -46,12 +47,14 @@ class DisplayManager: d.pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) self.mapdisplay.update_map(self.game.map) self.statsdisplay.update_player(self.game.player) + self.inventorydisplay.update_menu(self.game.inventory_menu) self.settingsmenudisplay.update_menu(self.game.settings_menu) self.logsdisplay.update_logs(self.game.logs) self.messagedisplay.update_message(self.game.message) def refresh(self) -> None: - if self.game.state == GameMode.PLAY: + if self.game.state == GameMode.PLAY \ + or self.game.state == GameMode.INVENTORY: # The map pad has already the good size self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.mapdisplay.pack.tile_width @@ -64,10 +67,15 @@ class DisplayManager: self.rows // 5 - 1, self.cols * 4 // 5) self.hbar.refresh(self.rows * 4 // 5, 0, 1, self.cols * 4 // 5) self.vbar.refresh(0, self.cols * 4 // 5, self.rows, 1) - if self.game.state == GameMode.MAINMENU: + if self.game.state == GameMode.INVENTORY: + self.inventorydisplay.refresh(self.rows // 10, + self.cols // 2, + 8 * self.rows // 10, + 2 * self.cols // 5) + elif self.game.state == GameMode.MAINMENU: self.mainmenudisplay.refresh(0, 0, self.rows, self.cols) - if self.game.state == GameMode.SETTINGS: - self.settingsmenudisplay.refresh(0, 0, self.rows, self.cols - 1) + elif self.game.state == GameMode.SETTINGS: + self.settingsmenudisplay.refresh(0, 0, self.rows, self.cols) if self.game.message: height, width = 0, 0 diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index 9cc3ad5..1dd2e4c 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -1,6 +1,6 @@ # Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later - +import curses from typing import List from squirrelbattle.menus import Menu, MainMenu @@ -24,8 +24,6 @@ class MenuDisplay(Display): # Menu values are printed in pad self.pad = self.newpad(self.trueheight, self.truewidth + 2) - for i in range(self.trueheight): - self.addstr(self.pad, i, 0, " " + self.values[i]) def update_pad(self) -> None: for i in range(self.trueheight): @@ -110,12 +108,22 @@ class MainMenuDisplay(Display): menuy, menux, min(self.menudisplay.preferred_height, self.height - menuy), menuwidth) -class VariableMenuDisplay(MenuDisplay): - """ - A class to display a menu in which each value is associated to a parameter - """ + +class InventoryDisplay(MenuDisplay): + def update_pad(self) -> None: + message = _("== INVENTORY ==") + self.addstr(self.pad, 0, (self.width - len(message)) // 2, message, + curses.A_BOLD | curses.A_ITALIC) + for i, item in enumerate(self.menu.values): + rep = self.pack[item.name.upper()] + selection = f"[{rep}]" if i == self.menu.position else f" {rep} " + self.addstr(self.pad, 2 + i, 0, selection + + " " + item.translated_name.capitalize()) + @property - def values(self) -> List[str]: - return [a[1][1] + (" : " - + (a[1][0]) - if a[1][0] else "") for a in self.menu.values] + def truewidth(self) -> int: + return max(1, self.height if hasattr(self, "height") else 10) + + @property + def trueheight(self) -> int: + return 2 + super().trueheight diff --git a/squirrelbattle/display/statsdisplay.py b/squirrelbattle/display/statsdisplay.py index da9213f..ac1a89c 100644 --- a/squirrelbattle/display/statsdisplay.py +++ b/squirrelbattle/display/statsdisplay.py @@ -31,8 +31,19 @@ class StatsDisplay(Display): self.player.dexterity, self.player.constitution) self.addstr(self.pad, 3, 0, string3) - inventory_str = _("Inventory:") + " " + "".join( - self.pack[item.name.upper()] for item in self.player.inventory) + inventory_str = _("Inventory:") + " " + # Stack items by type instead of displaying each item + item_types = [item.name for item in self.player.inventory] + item_types.sort(key=item_types.count, reverse=True) + printed_items = [] + for item in item_types: + if item in printed_items: + continue + count = item_types.count(item) + inventory_str += self.pack[item.upper()] + if count > 1: + inventory_str += f"x{count} " + printed_items.append(item) self.addstr(self.pad, 8, 0, inventory_str) if self.player.dead: diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 665dd6f..fcb4ce5 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -58,6 +58,7 @@ TexturePack.ASCII_PACK = TexturePack( TEDDY_BEAR='8', MERCHANT='M', SUNFLOWER='I', + BODY_SNATCH_POTION='S', ) TexturePack.SQUIRREL_PACK = TexturePack( @@ -79,4 +80,5 @@ TexturePack.SQUIRREL_PACK = TexturePack( TEDDY_BEAR='🧸', MERCHANT='🦜', SUNFLOWER='🌻', + BODY_SNATCH_POTION='🔀', ) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index e1326b9..ea5afeb 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -1,10 +1,12 @@ # Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later +from random import choice, randint from typing import Optional from .player import Player from ..interfaces import Entity, FightingEntity, Map +from ..translations import gettext as _ class Item(Entity): @@ -20,16 +22,26 @@ class Item(Entity): self.held = held self.held_by = held_by - def drop(self, y: int, x: int) -> None: + def drop(self) -> None: """ The item is dropped from the inventory onto the floor """ if self.held: self.held_by.inventory.remove(self) + self.map.add_entity(self) + self.move(self.held_by.y, self.held_by.x) self.held = False self.held_by = None - self.map.add_entity(self) - self.move(y, x) + + def use(self) -> None: + """ + Indicates what should be done when the item is used. + """ + + def equip(self) -> None: + """ + Indicates what should be done when the item is equipped. + """ def hold(self, player: "Player") -> None: """ @@ -55,8 +67,8 @@ class Heart(Item): """ healing: int - def __init__(self, healing: int = 5, *args, **kwargs): - super().__init__(name="heart", *args, **kwargs) + def __init__(self, name: str = "heart", healing: int = 5, *args, **kwargs): + super().__init__(name=name, *args, **kwargs) self.healing = healing def hold(self, player: "Player") -> None: @@ -81,26 +93,47 @@ class Bomb(Item): """ damage: int = 5 exploding: bool + owner: Optional["Player"] + tick: int - def __init__(self, damage: int = 5, exploding: bool = False, - *args, **kwargs): - super().__init__(name="bomb", *args, **kwargs) + def __init__(self, name: str = "bomb", damage: int = 5, + exploding: bool = False, *args, **kwargs): + super().__init__(name=name, *args, **kwargs) self.damage = damage self.exploding = exploding + self.tick = 4 + self.owner = None - def drop(self, x: int, y: int) -> None: - super().drop(x, y) - self.exploding = True + def use(self) -> None: + """ + When the bomb is used, throw it and explodes it. + """ + if self.held: + self.owner = self.held_by + super().drop() + self.exploding = True def act(self, m: Map) -> None: """ Special exploding action of the bomb """ if self.exploding: - for e in m.entities.copy(): - if abs(e.x - self.x) + abs(e.y - self.y) <= 1 and \ - isinstance(e, FightingEntity): - e.take_damage(self, self.damage) + if self.tick > 0: + # The bomb will explode in moves + self.tick -= 1 + else: + # The bomb is exploding. + # Each entity that is close to the bomb takes damages. + # The player earn XP if the entity was killed. + log_message = _("Bomb is exploding.") + for e in m.entities.copy(): + if abs(e.x - self.x) + abs(e.y - self.y) <= 3 and \ + isinstance(e, FightingEntity): + log_message += " " + e.take_damage(self, self.damage) + if e.dead: + self.owner.add_xp(randint(3, 7)) + m.logs.add_message(log_message) + m.entities.remove(self) def save_state(self) -> dict: """ @@ -110,7 +143,7 @@ class Bomb(Item): d["exploding"] = self.exploding d["damage"] = self.damage return d - + class Weapon(Item): """ Non-throwable items that improve player damage @@ -136,4 +169,35 @@ class Sword(Weapon) : def __init__(self, name: int, *args, **kwargs): super().__init__(*args, **kwargs) self.name = "sword" - + +class BodySnatchPotion(Item): + """ + The body-snatch potion allows to exchange all characteristics with a random + other entity. + """ + + def __init__(self, name: str = "body_snatch_potion", *args, **kwargs): + super().__init__(name=name, *args, **kwargs) + + def use(self) -> None: + """ + Find a valid random entity, then exchange characteristics. + """ + valid_entities = self.held_by.map.find_entities(FightingEntity) + valid_entities.remove(self.held_by) + entity = choice(valid_entities) + entity_state = entity.save_state() + player_state = self.held_by.save_state() + self.held_by.__dict__.update(entity_state) + entity.__dict__.update(player_state) + self.held_by.map.currenty, self.held_by.map.currentx = self.held_by.y,\ + self.held_by.x + + self.held_by.map.logs.add_message( + _("{player} exchanged its body with {entity}.").format( + player=self.held_by.translated_name.capitalize(), + entity=entity.translated_name)) + + self.held_by.recalculate_paths() + + self.held_by.inventory.remove(self) diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index 624f8a3..feff81a 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -1,7 +1,7 @@ # Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later -from random import choice +from random import shuffle from .player import Player from ..interfaces import FightingEntity, Map @@ -49,9 +49,13 @@ class Monster(FightingEntity): if not moved and self.distance_squared(target) <= 1: self.map.logs.add_message(self.hit(target)) else: - for _ in range(100): - if choice([self.move_up, self.move_down, - self.move_left, self.move_right])(): + # Move in a random direction + # If the direction is not available, try another one + moves = [self.move_up, self.move_down, + self.move_left, self.move_right] + shuffle(moves) + for move in moves: + if move(): break @@ -59,9 +63,9 @@ class Tiger(Monster): """ A tiger monster """ - def __init__(self, strength: int = 2, maxhealth: int = 20, - *args, **kwargs) -> None: - super().__init__(name="tiger", strength=strength, + def __init__(self, name: str = "tiger", strength: int = 2, + maxhealth: int = 20, *args, **kwargs) -> None: + super().__init__(name=name, strength=strength, maxhealth=maxhealth, *args, **kwargs) @@ -69,9 +73,9 @@ class Hedgehog(Monster): """ A really mean hedgehog monster """ - def __init__(self, strength: int = 3, maxhealth: int = 10, - *args, **kwargs) -> None: - super().__init__(name="hedgehog", strength=strength, + def __init__(self, name: str = "hedgehog", strength: int = 3, + maxhealth: int = 10, *args, **kwargs) -> None: + super().__init__(name=name, strength=strength, maxhealth=maxhealth, *args, **kwargs) @@ -79,9 +83,9 @@ class Rabbit(Monster): """ A rabbit monster """ - def __init__(self, strength: int = 1, maxhealth: int = 15, - *args, **kwargs) -> None: - super().__init__(name="rabbit", strength=strength, + def __init__(self, name: str = "rabbit", strength: int = 1, + maxhealth: int = 15, *args, **kwargs) -> None: + super().__init__(name=name, strength=strength, maxhealth=maxhealth, *args, **kwargs) @@ -89,7 +93,7 @@ class TeddyBear(Monster): """ A cute teddybear monster """ - def __init__(self, strength: int = 0, maxhealth: int = 50, - *args, **kwargs) -> None: - super().__init__(name="teddy_bear", strength=strength, + def __init__(self, name: str = "teddy_bear", strength: int = 0, + maxhealth: int = 50, *args, **kwargs) -> None: + super().__init__(name=name, strength=strength, maxhealth=maxhealth, *args, **kwargs) diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 6b7f084..c344f15 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -17,17 +17,24 @@ class Player(FightingEntity): paths: Dict[Tuple[int, int], Tuple[int, int]] hazel: int #It is the currency of this game - def __init__(self, maxhealth: int = 20, strength: int = 5, - intelligence: int = 1, charisma: int = 1, dexterity: int = 1, - constitution: int = 1, level: int = 1, current_xp: int = 0, - max_xp: int = 10, hazel: int = 42, *args, **kwargs) -> None: - super().__init__(name="player", maxhealth=maxhealth, strength=strength, + def __init__(self, name: str = "player", maxhealth: int = 20, + strength: int = 5, intelligence: int = 1, charisma: int = 1, + dexterity: int = 1, constitution: int = 1, level: int = 1, + current_xp: int = 0, max_xp: int = 10, inventory: list = None, + hazel: int = 42, *args, **kwargs) \ + -> None: + super().__init__(name=name, maxhealth=maxhealth, strength=strength, intelligence=intelligence, charisma=charisma, dexterity=dexterity, constitution=constitution, level=level, *args, **kwargs) self.current_xp = current_xp self.max_xp = max_xp - self.inventory = list() + self.inventory = inventory if inventory else list() + for i in range(len(self.inventory)): + if isinstance(self.inventory[i], dict): + entity_classes = self.get_all_entity_classes_in_a_dict() + item_class = entity_classes[self.inventory[i]["type"]] + self.inventory[i] = item_class(**self.inventory[i]) self.paths = dict() self.hazel = hazel @@ -130,4 +137,5 @@ class Player(FightingEntity): d = super().save_state() d["current_xp"] = self.current_xp d["max_xp"] = self.max_xp + d["inventory"] = [item.save_state() for item in self.inventory] return d diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index e563600..c62d199 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -38,6 +38,10 @@ class KeyValues(Enum): LEFT = auto() RIGHT = auto() ENTER = auto() + INVENTORY = auto() + USE = auto() + EQUIP = auto() + DROP = auto() SPACE = auto() T = auto() @@ -60,6 +64,14 @@ class KeyValues(Enum): return KeyValues.UP elif key == settings.KEY_ENTER: return KeyValues.ENTER + elif key == settings.KEY_INVENTORY: + return KeyValues.INVENTORY + elif key == settings.KEY_USE: + return KeyValues.USE + elif key == settings.KEY_EQUIP: + return KeyValues.EQUIP + elif key == settings.KEY_DROP: + return KeyValues.DROP elif key == ' ': return KeyValues.SPACE elif key == 't': diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index ec54dae..a465097 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -40,6 +40,7 @@ class Game: self.main_menu = menus.MainMenu() self.settings_menu = menus.SettingsMenu() self.settings_menu.update_values(self.settings) + self.inventory_menu = menus.InventoryMenu() self.logs = Logs() self.message = None @@ -48,13 +49,14 @@ class Game: Create a new game on the screen. """ # TODO generate a new map procedurally - self.map = Map.load(ResourceManager.get_asset_path("example_map_2.txt")) + self.map = Map.load(ResourceManager.get_asset_path("example_map.txt")) self.map.logs = self.logs self.logs.clear() self.player = Player() self.map.add_entity(self.player) self.player.move(self.map.start_y, self.map.start_x) self.map.spawn_random_entities(randint(3, 10)) + self.inventory_menu.update_player(self.player) def run(self, screen: Any) -> None: """ @@ -84,6 +86,8 @@ class Game: if self.state == GameMode.PLAY: self.handle_key_pressed_play(key) + elif self.state == GameMode.INVENTORY: + self.handle_key_pressed_inventory(key) elif self.state == GameMode.MAINMENU: self.handle_key_pressed_main_menu(key) elif self.state == GameMode.SETTINGS: @@ -106,6 +110,8 @@ class Game: elif key == KeyValues.RIGHT: if self.player.move_right(): self.map.tick() + elif key == KeyValues.INVENTORY: + self.state = GameMode.INVENTORY elif key == KeyValues.SPACE: self.state = GameMode.MAINMENU elif key == KeyValues.T : @@ -134,6 +140,29 @@ class Game: self.state = GameMode.STORE + def handle_key_pressed_inventory(self, key: KeyValues) -> None: + """ + In the inventory menu, we can interact with items or close the menu. + """ + if key == KeyValues.SPACE or key == KeyValues.INVENTORY: + self.state = GameMode.PLAY + elif key == KeyValues.UP: + self.inventory_menu.go_up() + elif key == KeyValues.DOWN: + self.inventory_menu.go_down() + if self.inventory_menu.values and not self.player.dead: + if key == KeyValues.USE: + self.inventory_menu.validate().use() + elif key == KeyValues.EQUIP: + self.inventory_menu.validate().equip() + elif key == KeyValues.DROP: + self.inventory_menu.validate().drop() + + # Ensure that the cursor has a good position + self.inventory_menu.position = min(self.inventory_menu.position, + len(self.inventory_menu.values) + - 1) + def handle_key_pressed_main_menu(self, key: KeyValues) -> None: """ In the main menu, we can navigate through options. diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index c1f3078..db56156 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -344,11 +344,11 @@ class Entity: """ Returns all entities subclasses """ - from squirrelbattle.entities.items import Heart, Bomb + from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart from squirrelbattle.entities.monsters import Tiger, Hedgehog, \ Rabbit, TeddyBear from squirrelbattle.entities.friendly import Merchant,Sunflower - return [Tiger, Bomb, Heart, Hedgehog, Rabbit, TeddyBear,Sunflower] + return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear,Sunflower,Tiger] @staticmethod def get_all_entity_classes_in_a_dict() -> dict: @@ -358,12 +358,13 @@ class Entity: from squirrelbattle.entities.player import Player from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \ TeddyBear - from squirrelbattle.entities.items import Bomb, Heart from squirrelbattle.entities.friendly import Merchant,Sunflower + from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart return { "Tiger": Tiger, "Bomb": Bomb, "Heart": Heart, + "BodySnatchPotion": BodySnatchPotion, "Hedgehog": Hedgehog, "Rabbit": Rabbit, "TeddyBear": TeddyBear, @@ -447,7 +448,7 @@ class FightingEntity(Entity): """ Returns a fighting entity's specific attributes """ - return ["maxhealth", "health", "level", "strength", + return ["name", "maxhealth", "health", "level", "strength", "intelligence", "charisma", "dexterity", "constitution"] def save_state(self) -> dict: diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index 0416b84..37ceb39 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,11 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" +<<<<<<< HEAD "POT-Creation-Date: 2020-12-01 17:10+0100\n" +======= +"POT-Creation-Date: 2020-12-05 14:46+0100\n" +>>>>>>> master "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,7 +21,75 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: squirrelbattle/tests/game_test.py:284 squirrelbattle/tests/game_test.py:287 +#: squirrelbattle/display/menudisplay.py:105 +msgid "== INVENTORY ==" +msgstr "== BESTAND ==" + +#: squirrelbattle/display/statsdisplay.py:34 +msgid "Inventory:" +msgstr "Bestand:" + +#: squirrelbattle/display/statsdisplay.py:50 +msgid "YOU ARE DEAD" +msgstr "SIE WURDEN GESTORBEN" + +#. The bomb is exploding. +#. Each entity that is close to the bomb takes damages. +#. The player earn XP if the entity was killed. +#: squirrelbattle/entities/items.py:128 +msgid "Bomb is exploding." +msgstr "Die Bombe explodiert." + +#: squirrelbattle/entities/items.py:172 +#, python-brace-format +msgid "{player} exchanged its body with {entity}." +msgstr "{player} täuscht seinem Körper mit {entity} aus." + +#: squirrelbattle/game.py:177 +msgid "" +"Some keys are missing in your save file.\n" +"Your save seems to be corrupt. It got deleted." +msgstr "" +"In Ihrer Speicherdatei fehlen einige Schlüssel.\n" +"Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht." + +#: squirrelbattle/game.py:185 +msgid "" +"No player was found on this map!\n" +"Maybe you died?" +msgstr "" +"Auf dieser Karte wurde kein Spieler gefunden!\n" +"Vielleicht sind Sie gestorben?" + +#: squirrelbattle/game.py:205 +msgid "" +"The JSON file is not correct.\n" +"Your save seems corrupted. It got deleted." +msgstr "" +"Die JSON-Datei ist nicht korrekt.\n" +"Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht." + +#: squirrelbattle/interfaces.py:400 +#, python-brace-format +msgid "{name} hits {opponent}." +msgstr "{name} schlägt {opponent}." + +#: squirrelbattle/interfaces.py:412 +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} nimmt {amount} Schadenspunkte." + +#: squirrelbattle/interfaces.py:414 +#, python-brace-format +msgid "{name} dies." +msgstr "{name} stirbt." + +#: squirrelbattle/menus.py:72 +msgid "Back" +msgstr "Zurück" + +#: squirrelbattle/tests/game_test.py:300 squirrelbattle/tests/game_test.py:303 +#: squirrelbattle/tests/game_test.py:306 #: squirrelbattle/tests/translations_test.py:16 #: squirrelbattle/tests/game_test.py:290 msgid "New game" @@ -80,40 +152,61 @@ msgid "Key to validate a menu" msgstr "Menütaste" #: squirrelbattle/tests/translations_test.py:45 +msgid "Key used to open the inventory" +msgstr "Bestandtaste" + +#: squirrelbattle/tests/translations_test.py:47 +msgid "Key used to use an item in the inventory" +msgstr "Taste um eines Objekts im Bestand zu verwenden" + +#: squirrelbattle/tests/translations_test.py:49 +msgid "Key used to equip an item in the inventory" +msgstr "Taste um eines Objekts im Bestand auszurüsten" + +#: squirrelbattle/tests/translations_test.py:51 +msgid "Key used to drop an item in the inventory" +msgstr "Taste um eines Objekts im Bestand zu werfen" + +#: squirrelbattle/tests/translations_test.py:53 msgid "Texture pack" msgstr "Textur-Packung" -#: squirrelbattle/tests/translations_test.py:46 +#: squirrelbattle/tests/translations_test.py:54 msgid "Language" msgstr "Sprache" -#: squirrelbattle/tests/translations_test.py:49 +#: squirrelbattle/tests/translations_test.py:57 msgid "player" msgstr "Spieler" -#: squirrelbattle/tests/translations_test.py:51 +#: squirrelbattle/tests/translations_test.py:59 msgid "tiger" msgstr "Tiger" -#: squirrelbattle/tests/translations_test.py:52 +#: squirrelbattle/tests/translations_test.py:60 msgid "hedgehog" msgstr "Igel" -#: squirrelbattle/tests/translations_test.py:53 +#: squirrelbattle/tests/translations_test.py:61 msgid "rabbit" msgstr "Kanninchen" -#: squirrelbattle/tests/translations_test.py:54 +#: squirrelbattle/tests/translations_test.py:62 msgid "teddy bear" msgstr "Teddybär" -#: squirrelbattle/tests/translations_test.py:56 +#: squirrelbattle/tests/translations_test.py:64 +msgid "body snatch potion" +msgstr "Leichenfleddererzaubertrank" + +#: squirrelbattle/tests/translations_test.py:65 msgid "bomb" msgstr "Bombe" -#: squirrelbattle/tests/translations_test.py:57 +#: squirrelbattle/tests/translations_test.py:66 msgid "heart" msgstr "Herz" +<<<<<<< HEAD #: squirrelbattle/display/statsdisplay.py:34 msgid "Inventory:" @@ -173,3 +266,5 @@ msgstr "Blumenmacht!!" #: squirrelbattle/entities/friendly.py:31 msgid "The sun is warm today" msgstr "Die Sonne ist warm heute" +======= +>>>>>>> master diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index fb9024e..6f4675e 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -8,7 +8,11 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" +<<<<<<< HEAD "POT-Creation-Date: 2020-12-01 17:10+0100\n" +======= +"POT-Creation-Date: 2020-12-05 14:46+0100\n" +>>>>>>> master "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,63 +21,35 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: squirrelbattle/display/menudisplay.py:105 +msgid "== INVENTORY ==" +msgstr "== INVENTAIRE ==" + #: squirrelbattle/display/statsdisplay.py:34 msgid "Inventory:" msgstr "Inventaire :" -#: squirrelbattle/display/statsdisplay.py:39 +#: squirrelbattle/display/statsdisplay.py:50 msgid "YOU ARE DEAD" msgstr "VOUS ÊTES MORT" +<<<<<<< HEAD #: squirrelbattle/interfaces.py:394 squirrelbattle/interfaces.py:398 #: squirrelbattle/interfaces.py:408 +======= +#. 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:128 +msgid "Bomb is exploding." +msgstr "La bombe explose." + +#: squirrelbattle/entities/items.py:172 #, python-brace-format -msgid "{name} hits {opponent}." -msgstr "{name} frappe {opponent}." +msgid "{player} exchanged its body with {entity}." +msgstr "{player} a échangé son corps avec {entity}." -#: squirrelbattle/interfaces.py:405 squirrelbattle/interfaces.py:410 -#: squirrelbattle/interfaces.py:420 -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} prend {amount} points de dégât." - -#: squirrelbattle/menus.py:45 squirrelbattle/tests/translations_test.py:14 -#: squirrelbattle/tests/game_test.py:284 squirrelbattle/tests/game_test.py:287 -#: squirrelbattle/tests/translations_test.py:16 -#: squirrelbattle/tests/game_test.py:290 -msgid "New game" -msgstr "Nouvelle partie" - -#: squirrelbattle/menus.py:46 squirrelbattle/tests/translations_test.py:15 -#: squirrelbattle/tests/translations_test.py:17 -msgid "Resume" -msgstr "Continuer" - -#: squirrelbattle/menus.py:47 squirrelbattle/tests/translations_test.py:17 -#: squirrelbattle/tests/translations_test.py:19 -msgid "Save" -msgstr "Sauvegarder" - -#: squirrelbattle/menus.py:48 squirrelbattle/tests/translations_test.py:16 -#: squirrelbattle/tests/translations_test.py:18 -msgid "Load" -msgstr "Charger" - -#: squirrelbattle/menus.py:49 squirrelbattle/tests/translations_test.py:18 -#: squirrelbattle/tests/translations_test.py:20 -msgid "Settings" -msgstr "Paramètres" - -#: squirrelbattle/menus.py:50 squirrelbattle/tests/translations_test.py:19 -#: squirrelbattle/tests/translations_test.py:21 -msgid "Exit" -msgstr "Quitter" - -#: squirrelbattle/menus.py:71 -msgid "Back" -msgstr "Retour" - -#: squirrelbattle/game.py:147 squirrelbattle/game.py:148 +#: squirrelbattle/game.py:177 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -81,7 +57,7 @@ msgstr "" "Certaines clés de votre ficher de sauvegarde sont manquantes.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/game.py:155 squirrelbattle/game.py:156 +#: squirrelbattle/game.py:185 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -89,7 +65,7 @@ msgstr "" "Aucun joueur n'a été trouvé sur la carte !\n" "Peut-être êtes-vous mort ?" -#: squirrelbattle/game.py:175 squirrelbattle/game.py:176 +#: squirrelbattle/game.py:205 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -97,72 +73,119 @@ msgstr "" "Le fichier JSON de sauvegarde est incorrect.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/settings.py:21 squirrelbattle/tests/translations_test.py:21 -#: squirrelbattle/tests/translations_test.py:25 +#: squirrelbattle/interfaces.py:400 +>>>>>>> master +#, python-brace-format +msgid "{name} hits {opponent}." +msgstr "{name} frappe {opponent}." + +<<<<<<< HEAD +#: squirrelbattle/interfaces.py:405 squirrelbattle/interfaces.py:410 +#: squirrelbattle/interfaces.py:420 +======= +#: squirrelbattle/interfaces.py:412 +>>>>>>> master +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} prend {amount} points de dégât." + +#: squirrelbattle/interfaces.py:414 +#, python-brace-format +msgid "{name} dies." +msgstr "{name} meurt." + +#: squirrelbattle/menus.py:72 +msgid "Back" +msgstr "Retour" + +#: squirrelbattle/tests/game_test.py:300 squirrelbattle/tests/game_test.py:303 +#: squirrelbattle/tests/game_test.py:306 +#: squirrelbattle/tests/translations_test.py:16 +#: squirrelbattle/tests/game_test.py:290 +msgid "New game" +msgstr "Nouvelle partie" + +#: squirrelbattle/tests/translations_test.py:17 +msgid "Resume" +msgstr "Continuer" + +#: squirrelbattle/tests/translations_test.py:18 +msgid "Load" +msgstr "Charger" + +#: squirrelbattle/tests/translations_test.py:19 +msgid "Save" +msgstr "Sauvegarder" + +#: squirrelbattle/tests/translations_test.py:20 +msgid "Settings" +msgstr "Paramètres" + +#: squirrelbattle/tests/translations_test.py:21 +msgid "Exit" +msgstr "Quitter" + #: squirrelbattle/tests/translations_test.py:27 msgid "Main key to move up" msgstr "Touche principale pour aller vers le haut" -#: squirrelbattle/settings.py:22 squirrelbattle/tests/translations_test.py:23 -#: squirrelbattle/tests/translations_test.py:27 #: squirrelbattle/tests/translations_test.py:29 msgid "Secondary key to move up" msgstr "Touche secondaire pour aller vers le haut" -#: squirrelbattle/settings.py:23 squirrelbattle/tests/translations_test.py:25 -#: squirrelbattle/tests/translations_test.py:29 #: squirrelbattle/tests/translations_test.py:31 msgid "Main key to move down" msgstr "Touche principale pour aller vers le bas" -#: squirrelbattle/settings.py:24 squirrelbattle/tests/translations_test.py:27 -#: squirrelbattle/tests/translations_test.py:31 #: squirrelbattle/tests/translations_test.py:33 msgid "Secondary key to move down" msgstr "Touche secondaire pour aller vers le bas" -#: squirrelbattle/settings.py:25 squirrelbattle/tests/translations_test.py:29 -#: squirrelbattle/tests/translations_test.py:33 #: squirrelbattle/tests/translations_test.py:35 msgid "Main key to move left" msgstr "Touche principale pour aller vers la gauche" -#: squirrelbattle/settings.py:26 squirrelbattle/tests/translations_test.py:31 -#: squirrelbattle/tests/translations_test.py:35 #: squirrelbattle/tests/translations_test.py:37 msgid "Secondary key to move left" msgstr "Touche secondaire pour aller vers la gauche" -#: squirrelbattle/settings.py:27 squirrelbattle/tests/translations_test.py:33 -#: squirrelbattle/tests/translations_test.py:37 #: squirrelbattle/tests/translations_test.py:39 msgid "Main key to move right" msgstr "Touche principale pour aller vers la droite" -#: squirrelbattle/settings.py:29 squirrelbattle/tests/translations_test.py:35 -#: squirrelbattle/tests/translations_test.py:39 #: squirrelbattle/tests/translations_test.py:41 msgid "Secondary key to move right" msgstr "Touche secondaire pour aller vers la droite" -#: squirrelbattle/settings.py:30 squirrelbattle/tests/translations_test.py:37 -#: squirrelbattle/tests/translations_test.py:41 #: squirrelbattle/tests/translations_test.py:43 msgid "Key to validate a menu" msgstr "Touche pour valider un menu" -#: squirrelbattle/settings.py:31 squirrelbattle/tests/translations_test.py:39 -#: squirrelbattle/tests/translations_test.py:43 #: squirrelbattle/tests/translations_test.py:45 +msgid "Key used to open the inventory" +msgstr "Touche utilisée pour ouvrir l'inventaire" + +#: squirrelbattle/tests/translations_test.py:47 +msgid "Key used to use an item in the inventory" +msgstr "Touche pour utiliser un objet de l'inventaire" + +#: squirrelbattle/tests/translations_test.py:49 +msgid "Key used to equip an item in the inventory" +msgstr "Touche pour équiper un objet de l'inventaire" + +#: squirrelbattle/tests/translations_test.py:51 +msgid "Key used to drop an item in the inventory" +msgstr "Touche pour jeter un objet de l'inventaire" + +#: squirrelbattle/tests/translations_test.py:53 msgid "Texture pack" msgstr "Pack de textures" -#: squirrelbattle/settings.py:32 squirrelbattle/tests/translations_test.py:40 -#: squirrelbattle/tests/translations_test.py:44 -#: squirrelbattle/tests/translations_test.py:46 +#: squirrelbattle/tests/translations_test.py:54 msgid "Language" msgstr "Langue" +<<<<<<< HEAD #: squirrelbattle/interfaces.py:407 squirrelbattle/interfaces.py:412 #: squirrelbattle/interfaces.py:422 #, python-brace-format @@ -171,36 +194,37 @@ msgstr "{name} meurt." #: squirrelbattle/tests/translations_test.py:47 #: squirrelbattle/tests/translations_test.py:49 +======= +#: squirrelbattle/tests/translations_test.py:57 +>>>>>>> master msgid "player" msgstr "joueur" -#: squirrelbattle/tests/translations_test.py:49 -#: squirrelbattle/tests/translations_test.py:51 +#: squirrelbattle/tests/translations_test.py:59 msgid "tiger" msgstr "tigre" -#: squirrelbattle/tests/translations_test.py:50 -#: squirrelbattle/tests/translations_test.py:52 +#: squirrelbattle/tests/translations_test.py:60 msgid "hedgehog" msgstr "hérisson" -#: squirrelbattle/tests/translations_test.py:51 -#: squirrelbattle/tests/translations_test.py:53 +#: squirrelbattle/tests/translations_test.py:61 msgid "rabbit" msgstr "lapin" -#: squirrelbattle/tests/translations_test.py:52 -#: squirrelbattle/tests/translations_test.py:54 +#: squirrelbattle/tests/translations_test.py:62 msgid "teddy bear" msgstr "nounours" -#: squirrelbattle/tests/translations_test.py:54 -#: squirrelbattle/tests/translations_test.py:56 +#: squirrelbattle/tests/translations_test.py:64 +msgid "body snatch potion" +msgstr "potion d'arrachage de corps" + +#: squirrelbattle/tests/translations_test.py:65 msgid "bomb" msgstr "bombe" -#: squirrelbattle/tests/translations_test.py:55 -#: squirrelbattle/tests/translations_test.py:57 +#: squirrelbattle/tests/translations_test.py:66 msgid "heart" msgstr "cœur" diff --git a/squirrelbattle/menus.py b/squirrelbattle/menus.py index 4fcfabe..d6946d0 100644 --- a/squirrelbattle/menus.py +++ b/squirrelbattle/menus.py @@ -5,6 +5,7 @@ from enum import Enum from typing import Any, Optional from .display.texturepack import TexturePack +from .entities.player import Player from .enums import GameMode, KeyValues, DisplayActions from .settings import Settings from .translations import gettext as _, Translator @@ -115,3 +116,14 @@ class SettingsMenu(Menu): game.settings.write_settings() self.waiting_for_key = False self.update_values(game.settings) + + +class InventoryMenu(Menu): + player: Player + + def update_player(self, player: Player) -> None: + self.player = player + + @property + def values(self) -> list: + return self.player.inventory diff --git a/squirrelbattle/settings.py b/squirrelbattle/settings.py index 3090679..4004645 100644 --- a/squirrelbattle/settings.py +++ b/squirrelbattle/settings.py @@ -27,6 +27,10 @@ class Settings: self.KEY_RIGHT_PRIMARY = ['d', 'Main key to move right'] self.KEY_RIGHT_SECONDARY = ['KEY_RIGHT', 'Secondary key to move right'] self.KEY_ENTER = ['\n', 'Key to validate a menu'] + self.KEY_INVENTORY = ['i', 'Key used to open the inventory'] + self.KEY_USE = ['u', 'Key used to use an item in the inventory'] + self.KEY_EQUIP = ['e', 'Key used to equip an item in the inventory'] + self.KEY_DROP = ['r', 'Key used to drop an item in the inventory'] self.TEXTURE_PACK = ['ascii', 'Texture pack'] self.LOCALE = [locale.getlocale()[0][:2], 'Language'] diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 371bfc7..2c72abd 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -3,7 +3,7 @@ import unittest -from squirrelbattle.entities.items import Bomb, Heart, Item +from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart, Item from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, TeddyBear from squirrelbattle.entities.player import Player from squirrelbattle.interfaces import Entity, Map @@ -97,12 +97,13 @@ class TestEntities(unittest.TestCase): self.assertFalse(item.held) item.hold(self.player) self.assertTrue(item.held) - item.drop(2, 6) - self.assertEqual(item.y, 2) + item.drop() + self.assertEqual(item.y, 1) self.assertEqual(item.x, 6) # Pick up item - self.player.move_down() + self.player.move_left() + self.player.move_right() self.assertTrue(item.held) self.assertEqual(item.held_by, self.player) self.assertIn(item, self.player.inventory) @@ -125,10 +126,14 @@ class TestEntities(unittest.TestCase): item.act(self.map) self.assertFalse(hedgehog.dead) self.assertFalse(teddy_bear.dead) - item.drop(42, 42) + self.player.move(42, 42) + item.hold(self.player) + item.use() self.assertEqual(item.y, 42) self.assertEqual(item.x, 42) - item.act(self.map) + # Wait for the explosion + for ignored in range(5): + item.act(self.map) self.assertTrue(hedgehog.dead) self.assertTrue(teddy_bear.dead) bomb_state = item.save_state() @@ -149,6 +154,24 @@ class TestEntities(unittest.TestCase): heart_state = item.save_state() self.assertEqual(heart_state["healing"], item.healing) + def test_body_snatch_potion(self) -> None: + """ + Test some random stuff with body snatch potions. + """ + item = BodySnatchPotion() + self.map.add_entity(item) + item.hold(self.player) + + tiger = Tiger(y=42, x=42) + self.map.add_entity(tiger) + + # The player becomes a tiger, and the tiger becomes a squirrel + item.use() + self.assertEqual(self.player.name, "tiger") + self.assertEqual(tiger.name, "player") + self.assertEqual(self.player.y, 42) + self.assertEqual(self.player.x, 42) + def test_players(self) -> None: """ Test some random stuff with players. diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index a23b6f9..887835b 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -7,6 +7,7 @@ import unittest from ..bootstrap import Bootstrap from ..display.display import Display from ..display.display_manager import DisplayManager +from ..entities.items import Bomb from ..entities.player import Player from ..enums import DisplayActions from ..game import Game, KeyValues, GameMode @@ -31,6 +32,9 @@ class TestGame(unittest.TestCase): """ Save a game and reload it. """ + bomb = Bomb() + self.game.map.add_entity(bomb) + bomb.hold(self.game.player) old_state = self.game.save_state() self.game.handle_key_pressed(KeyValues.DOWN) @@ -44,6 +48,9 @@ class TestGame(unittest.TestCase): new_state = self.game.save_state() self.assertEqual(old_state, new_state) + # Ensure that the bomb is loaded + self.assertTrue(self.game.player.inventory) + # Error on loading save with open(ResourceManager.get_config_path("save.json"), "w") as f: f.write("I am not a JSON file") @@ -107,6 +114,18 @@ class TestGame(unittest.TestCase): self.assertEqual(KeyValues.translate_key( self.game.settings.KEY_ENTER, self.game.settings), KeyValues.ENTER) + self.assertEqual(KeyValues.translate_key( + self.game.settings.KEY_INVENTORY, self.game.settings), + KeyValues.INVENTORY) + self.assertEqual(KeyValues.translate_key( + self.game.settings.KEY_USE, self.game.settings), + KeyValues.USE) + self.assertEqual(KeyValues.translate_key( + self.game.settings.KEY_EQUIP, self.game.settings), + KeyValues.EQUIP) + self.assertEqual(KeyValues.translate_key( + self.game.settings.KEY_DROP, self.game.settings), + KeyValues.DROP) self.assertEqual(KeyValues.translate_key(' ', self.game.settings), KeyValues.SPACE) self.assertEqual(KeyValues.translate_key('plop', self.game.settings), @@ -261,11 +280,8 @@ class TestGame(unittest.TestCase): self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a') # Navigate to "texture pack" - self.game.handle_key_pressed(KeyValues.DOWN) - self.game.handle_key_pressed(KeyValues.DOWN) - self.game.handle_key_pressed(KeyValues.DOWN) - self.game.handle_key_pressed(KeyValues.DOWN) - self.game.handle_key_pressed(KeyValues.DOWN) + for ignored in range(9): + self.game.handle_key_pressed(KeyValues.DOWN) # Change texture pack self.assertEqual(self.game.settings.TEXTURE_PACK, "ascii") @@ -337,3 +353,64 @@ class TestGame(unittest.TestCase): self.game.display_actions(DisplayActions.REFRESH) self.game.handle_key_pressed(None, "random key") self.assertIsNone(self.game.message) + + def test_inventory_menu(self) -> None: + """ + Open the inventory menu and interact with items. + """ + self.game.state = GameMode.PLAY + # Open and close the inventory + self.game.handle_key_pressed(KeyValues.INVENTORY) + self.assertEqual(self.game.state, GameMode.INVENTORY) + self.game.handle_key_pressed(KeyValues.SPACE) + self.assertEqual(self.game.state, GameMode.PLAY) + + # Add five bombs in the inventory + for ignored in range(5): + bomb = Bomb() + bomb.map = self.game.map + bomb.map.add_entity(bomb) + bomb.hold(self.game.player) + + self.game.handle_key_pressed(KeyValues.INVENTORY) + self.assertEqual(self.game.state, GameMode.INVENTORY) + + # Navigate in the menu + self.game.handle_key_pressed(KeyValues.DOWN) + self.game.handle_key_pressed(KeyValues.DOWN) + self.game.handle_key_pressed(KeyValues.DOWN) + self.assertEqual(self.game.inventory_menu.position, 3) + self.game.handle_key_pressed(KeyValues.DOWN) + self.game.handle_key_pressed(KeyValues.DOWN) + self.game.handle_key_pressed(KeyValues.UP) + self.game.handle_key_pressed(KeyValues.DOWN) + self.assertEqual(self.game.inventory_menu.position, 4) + + # Equip key does nothing + self.game.handle_key_pressed(KeyValues.EQUIP) + + # Drop an item + bomb = self.game.player.inventory[-1] + self.assertEqual(self.game.inventory_menu.validate(), bomb) + self.assertTrue(bomb.held) + self.assertEqual(bomb.held_by, self.game.player) + self.game.handle_key_pressed(KeyValues.DROP) + self.assertFalse(bomb.held) + self.assertIsNone(bomb.held_by) + self.assertIsNone(bomb.owner) + self.assertFalse(bomb.exploding) + self.assertEqual(bomb.y, self.game.player.y) + self.assertEqual(bomb.x, self.game.player.x) + + # Use the bomb + bomb = self.game.player.inventory[-1] + self.assertEqual(self.game.inventory_menu.validate(), bomb) + self.assertTrue(bomb.held) + self.assertEqual(bomb.held_by, self.game.player) + self.game.handle_key_pressed(KeyValues.USE) + self.assertFalse(bomb.held) + self.assertIsNone(bomb.held_by) + self.assertEqual(bomb.owner, self.game.player) + self.assertTrue(bomb.exploding) + self.assertEqual(bomb.y, self.game.player.y) + self.assertEqual(bomb.x, self.game.player.x) diff --git a/squirrelbattle/tests/settings_test.py b/squirrelbattle/tests/settings_test.py index b0d9739..06225b2 100644 --- a/squirrelbattle/tests/settings_test.py +++ b/squirrelbattle/tests/settings_test.py @@ -4,9 +4,13 @@ import unittest from squirrelbattle.settings import Settings +from squirrelbattle.translations import Translator class TestSettings(unittest.TestCase): + def setUp(self) -> None: + Translator.setlocale("en") + def test_settings(self) -> None: """ Ensure that settings are well loaded. diff --git a/squirrelbattle/tests/translations_test.py b/squirrelbattle/tests/translations_test.py index 6c18840..0cb39c5 100644 --- a/squirrelbattle/tests/translations_test.py +++ b/squirrelbattle/tests/translations_test.py @@ -42,6 +42,14 @@ class TestTranslations(unittest.TestCase): "Touche secondaire pour aller vers la droite") self.assertEqual(_("Key to validate a menu"), "Touche pour valider un menu") + self.assertEqual(_("Key used to open the inventory"), + "Touche utilisée pour ouvrir l'inventaire") + self.assertEqual(_("Key used to use an item in the inventory"), + "Touche pour utiliser un objet de l'inventaire") + self.assertEqual(_("Key used to equip an item in the inventory"), + "Touche pour équiper un objet de l'inventaire") + self.assertEqual(_("Key used to drop an item in the inventory"), + "Touche pour jeter un objet de l'inventaire") self.assertEqual(_("Texture pack"), "Pack de textures") self.assertEqual(_("Language"), "Langue") @@ -53,5 +61,6 @@ class TestTranslations(unittest.TestCase): self.assertEqual(_("rabbit"), "lapin") self.assertEqual(_("teddy bear"), "nounours") + self.assertEqual(_("body snatch potion"), "potion d'arrachage de corps") self.assertEqual(_("bomb"), "bombe") self.assertEqual(_("heart"), "cœur") diff --git a/squirrelbattle/translations.py b/squirrelbattle/translations.py index f532bb0..1e97df6 100644 --- a/squirrelbattle/translations.py +++ b/squirrelbattle/translations.py @@ -3,6 +3,7 @@ import gettext as gt import os +import re import subprocess from pathlib import Path from typing import Any, List @@ -53,6 +54,9 @@ class Translator: Analyse all strings in the project and extract them. """ for language in cls.SUPPORTED_LOCALES: + if language == "en": + # Don't translate the main language + continue file_name = Path(__file__).parent / "locale" / language \ / "LC_MESSAGES" / "squirrelbattle.po" args = ["find", "squirrelbattle", "-iname", "*.py"] @@ -65,9 +69,14 @@ class Translator: "--copyright-holder=ÿnérant, eichhornchen, " "nicomarg, charlse", "--msgid-bugs-address=squirrel-battle@crans.org", + "--sort-by-file", "-o", file_name] if file_name.is_file(): args.append("--join-existing") + with open(file_name, "r") as f: + content = f.read() + with open(file_name, "w") as f: + f.write(re.sub("#:.*\n", "", content)) print(f"Make {language} messages...") subprocess.Popen(args, stdin=find.stdout).wait() @@ -77,6 +86,8 @@ class Translator: Compile translation messages from source files. """ for language in cls.SUPPORTED_LOCALES: + if language == "en": + continue args = ["msgfmt", "--check-format", "-o", Path(__file__).parent / "locale" / language / "LC_MESSAGES" / "squirrelbattle.mo",