Merging master into village, conflicts were solved

This commit is contained in:
eichhornchen 2020-12-06 11:43:48 +01:00
parent 38842cee68
commit 866af98fe4
20 changed files with 574 additions and 168 deletions

View File

@ -11,7 +11,7 @@ with open("README.md", "r") as f:
long_description = f.read() long_description = f.read()
# Compile messages # Compile messages
for language in ["de", "en", "fr"]: for language in ["de", "fr"]:
args = ["msgfmt", "--check-format", args = ["msgfmt", "--check-format",
"-o", f"squirrelbattle/locale/{language}/LC_MESSAGES" "-o", f"squirrelbattle/locale/{language}/LC_MESSAGES"
"/squirrelbattle.mo", "/squirrelbattle.mo",

View File

@ -6,8 +6,8 @@ from squirrelbattle.display.display import VerticalSplit, HorizontalSplit
from squirrelbattle.display.mapdisplay import MapDisplay from squirrelbattle.display.mapdisplay import MapDisplay
from squirrelbattle.display.messagedisplay import MessageDisplay from squirrelbattle.display.messagedisplay import MessageDisplay
from squirrelbattle.display.statsdisplay import StatsDisplay from squirrelbattle.display.statsdisplay import StatsDisplay
from squirrelbattle.display.menudisplay import SettingsMenuDisplay, \ from squirrelbattle.display.menudisplay import MainMenuDisplay, \
MainMenuDisplay InventoryDisplay, SettingsMenuDisplay
from squirrelbattle.display.logsdisplay import LogsDisplay from squirrelbattle.display.logsdisplay import LogsDisplay
from squirrelbattle.display.texturepack import TexturePack from squirrelbattle.display.texturepack import TexturePack
from typing import Any from typing import Any
@ -23,10 +23,11 @@ class DisplayManager:
pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK)
self.mapdisplay = MapDisplay(screen, pack) self.mapdisplay = MapDisplay(screen, pack)
self.statsdisplay = StatsDisplay(screen, pack) self.statsdisplay = StatsDisplay(screen, pack)
self.logsdisplay = LogsDisplay(screen, pack)
self.inventorydisplay = InventoryDisplay(screen, pack)
self.mainmenudisplay = MainMenuDisplay(self.game.main_menu, self.mainmenudisplay = MainMenuDisplay(self.game.main_menu,
screen, pack) screen, pack)
self.settingsmenudisplay = SettingsMenuDisplay(screen, pack) self.settingsmenudisplay = SettingsMenuDisplay(screen, pack)
self.logsdisplay = LogsDisplay(screen, pack)
self.messagedisplay = MessageDisplay(screen=screen, pack=None) self.messagedisplay = MessageDisplay(screen=screen, pack=None)
self.hbar = HorizontalSplit(screen, pack) self.hbar = HorizontalSplit(screen, pack)
self.vbar = VerticalSplit(screen, pack) self.vbar = VerticalSplit(screen, pack)
@ -46,12 +47,14 @@ class DisplayManager:
d.pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) d.pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK)
self.mapdisplay.update_map(self.game.map) self.mapdisplay.update_map(self.game.map)
self.statsdisplay.update_player(self.game.player) self.statsdisplay.update_player(self.game.player)
self.inventorydisplay.update_menu(self.game.inventory_menu)
self.settingsmenudisplay.update_menu(self.game.settings_menu) self.settingsmenudisplay.update_menu(self.game.settings_menu)
self.logsdisplay.update_logs(self.game.logs) self.logsdisplay.update_logs(self.game.logs)
self.messagedisplay.update_message(self.game.message) self.messagedisplay.update_message(self.game.message)
def refresh(self) -> None: 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 # The map pad has already the good size
self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.mapdisplay.refresh(0, 0, self.rows * 4 // 5,
self.mapdisplay.pack.tile_width self.mapdisplay.pack.tile_width
@ -64,10 +67,15 @@ class DisplayManager:
self.rows // 5 - 1, self.cols * 4 // 5) self.rows // 5 - 1, self.cols * 4 // 5)
self.hbar.refresh(self.rows * 4 // 5, 0, 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) 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) self.mainmenudisplay.refresh(0, 0, self.rows, self.cols)
if self.game.state == GameMode.SETTINGS: elif self.game.state == GameMode.SETTINGS:
self.settingsmenudisplay.refresh(0, 0, self.rows, self.cols - 1) self.settingsmenudisplay.refresh(0, 0, self.rows, self.cols)
if self.game.message: if self.game.message:
height, width = 0, 0 height, width = 0, 0

View File

@ -1,6 +1,6 @@
# 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
from typing import List from typing import List
from squirrelbattle.menus import Menu, MainMenu from squirrelbattle.menus import Menu, MainMenu
@ -24,8 +24,6 @@ class MenuDisplay(Display):
# Menu values are printed in pad # Menu values are printed in pad
self.pad = self.newpad(self.trueheight, self.truewidth + 2) 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: def update_pad(self) -> None:
for i in range(self.trueheight): for i in range(self.trueheight):
@ -110,12 +108,22 @@ class MainMenuDisplay(Display):
menuy, menux, min(self.menudisplay.preferred_height, menuy, menux, min(self.menudisplay.preferred_height,
self.height - menuy), menuwidth) self.height - menuy), menuwidth)
class VariableMenuDisplay(MenuDisplay):
""" class InventoryDisplay(MenuDisplay):
A class to display a menu in which each value is associated to a parameter 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 @property
def values(self) -> List[str]: def truewidth(self) -> int:
return [a[1][1] + (" : " return max(1, self.height if hasattr(self, "height") else 10)
+ (a[1][0])
if a[1][0] else "") for a in self.menu.values] @property
def trueheight(self) -> int:
return 2 + super().trueheight

View File

@ -31,8 +31,19 @@ class StatsDisplay(Display):
self.player.dexterity, self.player.constitution) self.player.dexterity, self.player.constitution)
self.addstr(self.pad, 3, 0, string3) self.addstr(self.pad, 3, 0, string3)
inventory_str = _("Inventory:") + " " + "".join( inventory_str = _("Inventory:") + " "
self.pack[item.name.upper()] for item in self.player.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) self.addstr(self.pad, 8, 0, inventory_str)
if self.player.dead: if self.player.dead:

View File

@ -58,6 +58,7 @@ TexturePack.ASCII_PACK = TexturePack(
TEDDY_BEAR='8', TEDDY_BEAR='8',
MERCHANT='M', MERCHANT='M',
SUNFLOWER='I', SUNFLOWER='I',
BODY_SNATCH_POTION='S',
) )
TexturePack.SQUIRREL_PACK = TexturePack( TexturePack.SQUIRREL_PACK = TexturePack(
@ -79,4 +80,5 @@ TexturePack.SQUIRREL_PACK = TexturePack(
TEDDY_BEAR='🧸', TEDDY_BEAR='🧸',
MERCHANT='🦜', MERCHANT='🦜',
SUNFLOWER='🌻', SUNFLOWER='🌻',
BODY_SNATCH_POTION='🔀',
) )

View File

@ -1,10 +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
from random import choice, randint
from typing import Optional from typing import Optional
from .player import Player from .player import Player
from ..interfaces import Entity, FightingEntity, Map from ..interfaces import Entity, FightingEntity, Map
from ..translations import gettext as _
class Item(Entity): class Item(Entity):
@ -20,16 +22,26 @@ class Item(Entity):
self.held = held self.held = held
self.held_by = held_by 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 The item is dropped from the inventory onto the floor
""" """
if self.held: if self.held:
self.held_by.inventory.remove(self) 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 = False
self.held_by = None 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: def hold(self, player: "Player") -> None:
""" """
@ -55,8 +67,8 @@ class Heart(Item):
""" """
healing: int healing: int
def __init__(self, healing: int = 5, *args, **kwargs): def __init__(self, name: str = "heart", healing: int = 5, *args, **kwargs):
super().__init__(name="heart", *args, **kwargs) super().__init__(name=name, *args, **kwargs)
self.healing = healing self.healing = healing
def hold(self, player: "Player") -> None: def hold(self, player: "Player") -> None:
@ -81,26 +93,47 @@ class Bomb(Item):
""" """
damage: int = 5 damage: int = 5
exploding: bool exploding: bool
owner: Optional["Player"]
tick: int
def __init__(self, damage: int = 5, exploding: bool = False, def __init__(self, name: str = "bomb", damage: int = 5,
*args, **kwargs): exploding: bool = False, *args, **kwargs):
super().__init__(name="bomb", *args, **kwargs) super().__init__(name=name, *args, **kwargs)
self.damage = damage self.damage = damage
self.exploding = exploding self.exploding = exploding
self.tick = 4
self.owner = None
def drop(self, x: int, y: int) -> None: def use(self) -> None:
super().drop(x, y) """
self.exploding = True 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: def act(self, m: Map) -> None:
""" """
Special exploding action of the bomb Special exploding action of the bomb
""" """
if self.exploding: if self.exploding:
for e in m.entities.copy(): if self.tick > 0:
if abs(e.x - self.x) + abs(e.y - self.y) <= 1 and \ # The bomb will explode in <tick> moves
isinstance(e, FightingEntity): self.tick -= 1
e.take_damage(self, self.damage) 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: def save_state(self) -> dict:
""" """
@ -110,7 +143,7 @@ class Bomb(Item):
d["exploding"] = self.exploding d["exploding"] = self.exploding
d["damage"] = self.damage d["damage"] = self.damage
return d return d
class Weapon(Item): class Weapon(Item):
""" """
Non-throwable items that improve player damage Non-throwable items that improve player damage
@ -136,4 +169,35 @@ class Sword(Weapon) :
def __init__(self, name: int, *args, **kwargs): def __init__(self, name: int, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.name = "sword" 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)

View File

@ -1,7 +1,7 @@
# 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
from random import choice from random import shuffle
from .player import Player from .player import Player
from ..interfaces import FightingEntity, Map from ..interfaces import FightingEntity, Map
@ -49,9 +49,13 @@ class Monster(FightingEntity):
if not moved and self.distance_squared(target) <= 1: if not moved and self.distance_squared(target) <= 1:
self.map.logs.add_message(self.hit(target)) self.map.logs.add_message(self.hit(target))
else: else:
for _ in range(100): # Move in a random direction
if choice([self.move_up, self.move_down, # If the direction is not available, try another one
self.move_left, self.move_right])(): moves = [self.move_up, self.move_down,
self.move_left, self.move_right]
shuffle(moves)
for move in moves:
if move():
break break
@ -59,9 +63,9 @@ class Tiger(Monster):
""" """
A tiger monster A tiger monster
""" """
def __init__(self, strength: int = 2, maxhealth: int = 20, def __init__(self, name: str = "tiger", strength: int = 2,
*args, **kwargs) -> None: maxhealth: int = 20, *args, **kwargs) -> None:
super().__init__(name="tiger", strength=strength, super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs) maxhealth=maxhealth, *args, **kwargs)
@ -69,9 +73,9 @@ class Hedgehog(Monster):
""" """
A really mean hedgehog monster A really mean hedgehog monster
""" """
def __init__(self, strength: int = 3, maxhealth: int = 10, def __init__(self, name: str = "hedgehog", strength: int = 3,
*args, **kwargs) -> None: maxhealth: int = 10, *args, **kwargs) -> None:
super().__init__(name="hedgehog", strength=strength, super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs) maxhealth=maxhealth, *args, **kwargs)
@ -79,9 +83,9 @@ class Rabbit(Monster):
""" """
A rabbit monster A rabbit monster
""" """
def __init__(self, strength: int = 1, maxhealth: int = 15, def __init__(self, name: str = "rabbit", strength: int = 1,
*args, **kwargs) -> None: maxhealth: int = 15, *args, **kwargs) -> None:
super().__init__(name="rabbit", strength=strength, super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs) maxhealth=maxhealth, *args, **kwargs)
@ -89,7 +93,7 @@ class TeddyBear(Monster):
""" """
A cute teddybear monster A cute teddybear monster
""" """
def __init__(self, strength: int = 0, maxhealth: int = 50, def __init__(self, name: str = "teddy_bear", strength: int = 0,
*args, **kwargs) -> None: maxhealth: int = 50, *args, **kwargs) -> None:
super().__init__(name="teddy_bear", strength=strength, super().__init__(name=name, strength=strength,
maxhealth=maxhealth, *args, **kwargs) maxhealth=maxhealth, *args, **kwargs)

View File

@ -17,17 +17,24 @@ class Player(FightingEntity):
paths: Dict[Tuple[int, int], Tuple[int, int]] paths: Dict[Tuple[int, int], Tuple[int, int]]
hazel: int #It is the currency of this game hazel: int #It is the currency of this game
def __init__(self, maxhealth: int = 20, strength: int = 5, def __init__(self, name: str = "player", maxhealth: int = 20,
intelligence: int = 1, charisma: int = 1, dexterity: int = 1, strength: int = 5, intelligence: int = 1, charisma: int = 1,
constitution: int = 1, level: int = 1, current_xp: int = 0, dexterity: int = 1, constitution: int = 1, level: int = 1,
max_xp: int = 10, hazel: int = 42, *args, **kwargs) -> None: current_xp: int = 0, max_xp: int = 10, inventory: list = None,
super().__init__(name="player", maxhealth=maxhealth, strength=strength, hazel: int = 42, *args, **kwargs) \
-> None:
super().__init__(name=name, maxhealth=maxhealth, strength=strength,
intelligence=intelligence, charisma=charisma, intelligence=intelligence, charisma=charisma,
dexterity=dexterity, constitution=constitution, dexterity=dexterity, constitution=constitution,
level=level, *args, **kwargs) level=level, *args, **kwargs)
self.current_xp = current_xp self.current_xp = current_xp
self.max_xp = max_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.paths = dict()
self.hazel = hazel self.hazel = hazel
@ -130,4 +137,5 @@ class Player(FightingEntity):
d = super().save_state() d = super().save_state()
d["current_xp"] = self.current_xp d["current_xp"] = self.current_xp
d["max_xp"] = self.max_xp d["max_xp"] = self.max_xp
d["inventory"] = [item.save_state() for item in self.inventory]
return d return d

View File

@ -38,6 +38,10 @@ class KeyValues(Enum):
LEFT = auto() LEFT = auto()
RIGHT = auto() RIGHT = auto()
ENTER = auto() ENTER = auto()
INVENTORY = auto()
USE = auto()
EQUIP = auto()
DROP = auto()
SPACE = auto() SPACE = auto()
T = auto() T = auto()
@ -60,6 +64,14 @@ class KeyValues(Enum):
return KeyValues.UP return KeyValues.UP
elif key == settings.KEY_ENTER: elif key == settings.KEY_ENTER:
return KeyValues.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 == ' ': elif key == ' ':
return KeyValues.SPACE return KeyValues.SPACE
elif key == 't': elif key == 't':

View File

@ -40,6 +40,7 @@ class Game:
self.main_menu = menus.MainMenu() self.main_menu = menus.MainMenu()
self.settings_menu = menus.SettingsMenu() self.settings_menu = menus.SettingsMenu()
self.settings_menu.update_values(self.settings) self.settings_menu.update_values(self.settings)
self.inventory_menu = menus.InventoryMenu()
self.logs = Logs() self.logs = Logs()
self.message = None self.message = None
@ -48,13 +49,14 @@ class Game:
Create a new game on the screen. Create a new game on the screen.
""" """
# TODO generate a new map procedurally # 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.map.logs = self.logs
self.logs.clear() self.logs.clear()
self.player = Player() self.player = Player()
self.map.add_entity(self.player) self.map.add_entity(self.player)
self.player.move(self.map.start_y, self.map.start_x) self.player.move(self.map.start_y, self.map.start_x)
self.map.spawn_random_entities(randint(3, 10)) self.map.spawn_random_entities(randint(3, 10))
self.inventory_menu.update_player(self.player)
def run(self, screen: Any) -> None: def run(self, screen: Any) -> None:
""" """
@ -84,6 +86,8 @@ class Game:
if self.state == GameMode.PLAY: if self.state == GameMode.PLAY:
self.handle_key_pressed_play(key) self.handle_key_pressed_play(key)
elif self.state == GameMode.INVENTORY:
self.handle_key_pressed_inventory(key)
elif self.state == GameMode.MAINMENU: elif self.state == GameMode.MAINMENU:
self.handle_key_pressed_main_menu(key) self.handle_key_pressed_main_menu(key)
elif self.state == GameMode.SETTINGS: elif self.state == GameMode.SETTINGS:
@ -106,6 +110,8 @@ class Game:
elif key == KeyValues.RIGHT: elif key == KeyValues.RIGHT:
if self.player.move_right(): if self.player.move_right():
self.map.tick() self.map.tick()
elif key == KeyValues.INVENTORY:
self.state = GameMode.INVENTORY
elif key == KeyValues.SPACE: elif key == KeyValues.SPACE:
self.state = GameMode.MAINMENU self.state = GameMode.MAINMENU
elif key == KeyValues.T : elif key == KeyValues.T :
@ -134,6 +140,29 @@ class Game:
self.state = GameMode.STORE 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: def handle_key_pressed_main_menu(self, key: KeyValues) -> None:
""" """
In the main menu, we can navigate through options. In the main menu, we can navigate through options.

View File

@ -344,11 +344,11 @@ class Entity:
""" """
Returns all entities subclasses 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, \ from squirrelbattle.entities.monsters import Tiger, Hedgehog, \
Rabbit, TeddyBear Rabbit, TeddyBear
from squirrelbattle.entities.friendly import Merchant,Sunflower 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 @staticmethod
def get_all_entity_classes_in_a_dict() -> dict: def get_all_entity_classes_in_a_dict() -> dict:
@ -358,12 +358,13 @@ class Entity:
from squirrelbattle.entities.player import Player from squirrelbattle.entities.player import Player
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \ from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \
TeddyBear TeddyBear
from squirrelbattle.entities.items import Bomb, Heart
from squirrelbattle.entities.friendly import Merchant,Sunflower from squirrelbattle.entities.friendly import Merchant,Sunflower
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart
return { return {
"Tiger": Tiger, "Tiger": Tiger,
"Bomb": Bomb, "Bomb": Bomb,
"Heart": Heart, "Heart": Heart,
"BodySnatchPotion": BodySnatchPotion,
"Hedgehog": Hedgehog, "Hedgehog": Hedgehog,
"Rabbit": Rabbit, "Rabbit": Rabbit,
"TeddyBear": TeddyBear, "TeddyBear": TeddyBear,
@ -447,7 +448,7 @@ class FightingEntity(Entity):
""" """
Returns a fighting entity's specific attributes Returns a fighting entity's specific attributes
""" """
return ["maxhealth", "health", "level", "strength", return ["name", "maxhealth", "health", "level", "strength",
"intelligence", "charisma", "dexterity", "constitution"] "intelligence", "charisma", "dexterity", "constitution"]
def save_state(self) -> dict: def save_state(self) -> dict:

View File

@ -8,7 +8,11 @@ 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"
<<<<<<< HEAD
"POT-Creation-Date: 2020-12-01 17:10+0100\n" "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" "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,7 +21,75 @@ 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"
#: 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/translations_test.py:16
#: squirrelbattle/tests/game_test.py:290 #: squirrelbattle/tests/game_test.py:290
msgid "New game" msgid "New game"
@ -80,40 +152,61 @@ msgid "Key to validate a menu"
msgstr "Menütaste" msgstr "Menütaste"
#: squirrelbattle/tests/translations_test.py:45 #: 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" msgid "Texture pack"
msgstr "Textur-Packung" msgstr "Textur-Packung"
#: squirrelbattle/tests/translations_test.py:46 #: squirrelbattle/tests/translations_test.py:54
msgid "Language" msgid "Language"
msgstr "Sprache" msgstr "Sprache"
#: squirrelbattle/tests/translations_test.py:49 #: squirrelbattle/tests/translations_test.py:57
msgid "player" msgid "player"
msgstr "Spieler" msgstr "Spieler"
#: squirrelbattle/tests/translations_test.py:51 #: squirrelbattle/tests/translations_test.py:59
msgid "tiger" msgid "tiger"
msgstr "Tiger" msgstr "Tiger"
#: squirrelbattle/tests/translations_test.py:52 #: squirrelbattle/tests/translations_test.py:60
msgid "hedgehog" msgid "hedgehog"
msgstr "Igel" msgstr "Igel"
#: squirrelbattle/tests/translations_test.py:53 #: squirrelbattle/tests/translations_test.py:61
msgid "rabbit" msgid "rabbit"
msgstr "Kanninchen" msgstr "Kanninchen"
#: squirrelbattle/tests/translations_test.py:54 #: squirrelbattle/tests/translations_test.py:62
msgid "teddy bear" msgid "teddy bear"
msgstr "Teddybär" 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" msgid "bomb"
msgstr "Bombe" msgstr "Bombe"
#: squirrelbattle/tests/translations_test.py:57 #: squirrelbattle/tests/translations_test.py:66
msgid "heart" msgid "heart"
msgstr "Herz" msgstr "Herz"
<<<<<<< HEAD
#: squirrelbattle/display/statsdisplay.py:34 #: squirrelbattle/display/statsdisplay.py:34
msgid "Inventory:" msgid "Inventory:"
@ -173,3 +266,5 @@ msgstr "Blumenmacht!!"
#: squirrelbattle/entities/friendly.py:31 #: squirrelbattle/entities/friendly.py:31
msgid "The sun is warm today" msgid "The sun is warm today"
msgstr "Die Sonne ist warm heute" msgstr "Die Sonne ist warm heute"
=======
>>>>>>> master

View File

@ -8,7 +8,11 @@ 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"
<<<<<<< HEAD
"POT-Creation-Date: 2020-12-01 17:10+0100\n" "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" "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,63 +21,35 @@ 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"
#: squirrelbattle/display/menudisplay.py:105
msgid "== INVENTORY =="
msgstr "== INVENTAIRE =="
#: squirrelbattle/display/statsdisplay.py:34 #: squirrelbattle/display/statsdisplay.py:34
msgid "Inventory:" msgid "Inventory:"
msgstr "Inventaire :" msgstr "Inventaire :"
#: squirrelbattle/display/statsdisplay.py:39 #: squirrelbattle/display/statsdisplay.py:50
msgid "YOU ARE DEAD" msgid "YOU ARE DEAD"
msgstr "VOUS ÊTES MORT" msgstr "VOUS ÊTES MORT"
<<<<<<< HEAD
#: squirrelbattle/interfaces.py:394 squirrelbattle/interfaces.py:398 #: squirrelbattle/interfaces.py:394 squirrelbattle/interfaces.py:398
#: squirrelbattle/interfaces.py:408 #: 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 #, python-brace-format
msgid "{name} hits {opponent}." msgid "{player} exchanged its body with {entity}."
msgstr "{name} frappe {opponent}." msgstr "{player} a échangé son corps avec {entity}."
#: squirrelbattle/interfaces.py:405 squirrelbattle/interfaces.py:410 #: squirrelbattle/game.py:177
#: 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
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."
@ -81,7 +57,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:155 squirrelbattle/game.py:156 #: squirrelbattle/game.py:185
msgid "" msgid ""
"No player was found on this map!\n" "No player was found on this map!\n"
"Maybe you died?" "Maybe you died?"
@ -89,7 +65,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:175 squirrelbattle/game.py:176 #: squirrelbattle/game.py:205
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."
@ -97,72 +73,119 @@ 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/settings.py:21 squirrelbattle/tests/translations_test.py:21 #: squirrelbattle/interfaces.py:400
#: squirrelbattle/tests/translations_test.py:25 >>>>>>> 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 #: squirrelbattle/tests/translations_test.py:27
msgid "Main key to move up" msgid "Main key to move up"
msgstr "Touche principale pour aller vers le haut" 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 #: squirrelbattle/tests/translations_test.py:29
msgid "Secondary key to move up" msgid "Secondary key to move up"
msgstr "Touche secondaire pour aller vers le haut" 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 #: squirrelbattle/tests/translations_test.py:31
msgid "Main key to move down" msgid "Main key to move down"
msgstr "Touche principale pour aller vers le bas" 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 #: squirrelbattle/tests/translations_test.py:33
msgid "Secondary key to move down" msgid "Secondary key to move down"
msgstr "Touche secondaire pour aller vers le bas" 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 #: squirrelbattle/tests/translations_test.py:35
msgid "Main key to move left" msgid "Main key to move left"
msgstr "Touche principale pour aller vers la gauche" 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 #: squirrelbattle/tests/translations_test.py:37
msgid "Secondary key to move left" msgid "Secondary key to move left"
msgstr "Touche secondaire pour aller vers la gauche" 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 #: squirrelbattle/tests/translations_test.py:39
msgid "Main key to move right" msgid "Main key to move right"
msgstr "Touche principale pour aller vers la droite" 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 #: squirrelbattle/tests/translations_test.py:41
msgid "Secondary key to move right" msgid "Secondary key to move right"
msgstr "Touche secondaire pour aller vers la droite" 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 #: squirrelbattle/tests/translations_test.py:43
msgid "Key to validate a menu" msgid "Key to validate a menu"
msgstr "Touche pour valider un 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 #: 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" msgid "Texture pack"
msgstr "Pack de textures" msgstr "Pack de textures"
#: squirrelbattle/settings.py:32 squirrelbattle/tests/translations_test.py:40 #: squirrelbattle/tests/translations_test.py:54
#: squirrelbattle/tests/translations_test.py:44
#: squirrelbattle/tests/translations_test.py:46
msgid "Language" msgid "Language"
msgstr "Langue" msgstr "Langue"
<<<<<<< HEAD
#: squirrelbattle/interfaces.py:407 squirrelbattle/interfaces.py:412 #: squirrelbattle/interfaces.py:407 squirrelbattle/interfaces.py:412
#: squirrelbattle/interfaces.py:422 #: squirrelbattle/interfaces.py:422
#, python-brace-format #, python-brace-format
@ -171,36 +194,37 @@ msgstr "{name} meurt."
#: squirrelbattle/tests/translations_test.py:47 #: squirrelbattle/tests/translations_test.py:47
#: squirrelbattle/tests/translations_test.py:49 #: squirrelbattle/tests/translations_test.py:49
=======
#: squirrelbattle/tests/translations_test.py:57
>>>>>>> master
msgid "player" msgid "player"
msgstr "joueur" msgstr "joueur"
#: squirrelbattle/tests/translations_test.py:49 #: squirrelbattle/tests/translations_test.py:59
#: squirrelbattle/tests/translations_test.py:51
msgid "tiger" msgid "tiger"
msgstr "tigre" msgstr "tigre"
#: squirrelbattle/tests/translations_test.py:50 #: squirrelbattle/tests/translations_test.py:60
#: squirrelbattle/tests/translations_test.py:52
msgid "hedgehog" msgid "hedgehog"
msgstr "hérisson" msgstr "hérisson"
#: squirrelbattle/tests/translations_test.py:51 #: squirrelbattle/tests/translations_test.py:61
#: squirrelbattle/tests/translations_test.py:53
msgid "rabbit" msgid "rabbit"
msgstr "lapin" msgstr "lapin"
#: squirrelbattle/tests/translations_test.py:52 #: squirrelbattle/tests/translations_test.py:62
#: squirrelbattle/tests/translations_test.py:54
msgid "teddy bear" msgid "teddy bear"
msgstr "nounours" msgstr "nounours"
#: squirrelbattle/tests/translations_test.py:54 #: squirrelbattle/tests/translations_test.py:64
#: squirrelbattle/tests/translations_test.py:56 msgid "body snatch potion"
msgstr "potion d'arrachage de corps"
#: squirrelbattle/tests/translations_test.py:65
msgid "bomb" msgid "bomb"
msgstr "bombe" msgstr "bombe"
#: squirrelbattle/tests/translations_test.py:55 #: squirrelbattle/tests/translations_test.py:66
#: squirrelbattle/tests/translations_test.py:57
msgid "heart" msgid "heart"
msgstr "cœur" msgstr "cœur"

View File

@ -5,6 +5,7 @@ from enum import Enum
from typing import Any, Optional from typing import Any, Optional
from .display.texturepack import TexturePack from .display.texturepack import TexturePack
from .entities.player import Player
from .enums import GameMode, KeyValues, DisplayActions from .enums import GameMode, KeyValues, DisplayActions
from .settings import Settings from .settings import Settings
from .translations import gettext as _, Translator from .translations import gettext as _, Translator
@ -115,3 +116,14 @@ class SettingsMenu(Menu):
game.settings.write_settings() game.settings.write_settings()
self.waiting_for_key = False self.waiting_for_key = False
self.update_values(game.settings) 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

View File

@ -27,6 +27,10 @@ class Settings:
self.KEY_RIGHT_PRIMARY = ['d', 'Main key to move right'] self.KEY_RIGHT_PRIMARY = ['d', 'Main key to move right']
self.KEY_RIGHT_SECONDARY = ['KEY_RIGHT', 'Secondary 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_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.TEXTURE_PACK = ['ascii', 'Texture pack']
self.LOCALE = [locale.getlocale()[0][:2], 'Language'] self.LOCALE = [locale.getlocale()[0][:2], 'Language']

View File

@ -3,7 +3,7 @@
import unittest 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.monsters import Tiger, Hedgehog, Rabbit, TeddyBear
from squirrelbattle.entities.player import Player from squirrelbattle.entities.player import Player
from squirrelbattle.interfaces import Entity, Map from squirrelbattle.interfaces import Entity, Map
@ -97,12 +97,13 @@ class TestEntities(unittest.TestCase):
self.assertFalse(item.held) self.assertFalse(item.held)
item.hold(self.player) item.hold(self.player)
self.assertTrue(item.held) self.assertTrue(item.held)
item.drop(2, 6) item.drop()
self.assertEqual(item.y, 2) self.assertEqual(item.y, 1)
self.assertEqual(item.x, 6) self.assertEqual(item.x, 6)
# Pick up item # Pick up item
self.player.move_down() self.player.move_left()
self.player.move_right()
self.assertTrue(item.held) self.assertTrue(item.held)
self.assertEqual(item.held_by, self.player) self.assertEqual(item.held_by, self.player)
self.assertIn(item, self.player.inventory) self.assertIn(item, self.player.inventory)
@ -125,10 +126,14 @@ class TestEntities(unittest.TestCase):
item.act(self.map) item.act(self.map)
self.assertFalse(hedgehog.dead) self.assertFalse(hedgehog.dead)
self.assertFalse(teddy_bear.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.y, 42)
self.assertEqual(item.x, 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(hedgehog.dead)
self.assertTrue(teddy_bear.dead) self.assertTrue(teddy_bear.dead)
bomb_state = item.save_state() bomb_state = item.save_state()
@ -149,6 +154,24 @@ class TestEntities(unittest.TestCase):
heart_state = item.save_state() heart_state = item.save_state()
self.assertEqual(heart_state["healing"], item.healing) 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: def test_players(self) -> None:
""" """
Test some random stuff with players. Test some random stuff with players.

View File

@ -7,6 +7,7 @@ import unittest
from ..bootstrap import Bootstrap from ..bootstrap import Bootstrap
from ..display.display import Display from ..display.display import Display
from ..display.display_manager import DisplayManager from ..display.display_manager import DisplayManager
from ..entities.items import Bomb
from ..entities.player import Player from ..entities.player import Player
from ..enums import DisplayActions from ..enums import DisplayActions
from ..game import Game, KeyValues, GameMode from ..game import Game, KeyValues, GameMode
@ -31,6 +32,9 @@ class TestGame(unittest.TestCase):
""" """
Save a game and reload it. 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() old_state = self.game.save_state()
self.game.handle_key_pressed(KeyValues.DOWN) self.game.handle_key_pressed(KeyValues.DOWN)
@ -44,6 +48,9 @@ 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)
# Ensure that the bomb is loaded
self.assertTrue(self.game.player.inventory)
# Error on loading save # Error on loading save
with open(ResourceManager.get_config_path("save.json"), "w") as f: with open(ResourceManager.get_config_path("save.json"), "w") as f:
f.write("I am not a JSON file") f.write("I am not a JSON file")
@ -107,6 +114,18 @@ class TestGame(unittest.TestCase):
self.assertEqual(KeyValues.translate_key( self.assertEqual(KeyValues.translate_key(
self.game.settings.KEY_ENTER, self.game.settings), self.game.settings.KEY_ENTER, self.game.settings),
KeyValues.ENTER) 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), self.assertEqual(KeyValues.translate_key(' ', self.game.settings),
KeyValues.SPACE) KeyValues.SPACE)
self.assertEqual(KeyValues.translate_key('plop', self.game.settings), 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') self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a')
# Navigate to "texture pack" # Navigate to "texture pack"
self.game.handle_key_pressed(KeyValues.DOWN) for ignored in range(9):
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)
# Change texture pack # Change texture pack
self.assertEqual(self.game.settings.TEXTURE_PACK, "ascii") self.assertEqual(self.game.settings.TEXTURE_PACK, "ascii")
@ -337,3 +353,64 @@ class TestGame(unittest.TestCase):
self.game.display_actions(DisplayActions.REFRESH) self.game.display_actions(DisplayActions.REFRESH)
self.game.handle_key_pressed(None, "random key") self.game.handle_key_pressed(None, "random key")
self.assertIsNone(self.game.message) 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)

View File

@ -4,9 +4,13 @@
import unittest import unittest
from squirrelbattle.settings import Settings from squirrelbattle.settings import Settings
from squirrelbattle.translations import Translator
class TestSettings(unittest.TestCase): class TestSettings(unittest.TestCase):
def setUp(self) -> None:
Translator.setlocale("en")
def test_settings(self) -> None: def test_settings(self) -> None:
""" """
Ensure that settings are well loaded. Ensure that settings are well loaded.

View File

@ -42,6 +42,14 @@ class TestTranslations(unittest.TestCase):
"Touche secondaire pour aller vers la droite") "Touche secondaire pour aller vers la droite")
self.assertEqual(_("Key to validate a menu"), self.assertEqual(_("Key to validate a menu"),
"Touche pour valider un 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(_("Texture pack"), "Pack de textures")
self.assertEqual(_("Language"), "Langue") self.assertEqual(_("Language"), "Langue")
@ -53,5 +61,6 @@ class TestTranslations(unittest.TestCase):
self.assertEqual(_("rabbit"), "lapin") self.assertEqual(_("rabbit"), "lapin")
self.assertEqual(_("teddy bear"), "nounours") self.assertEqual(_("teddy bear"), "nounours")
self.assertEqual(_("body snatch potion"), "potion d'arrachage de corps")
self.assertEqual(_("bomb"), "bombe") self.assertEqual(_("bomb"), "bombe")
self.assertEqual(_("heart"), "cœur") self.assertEqual(_("heart"), "cœur")

View File

@ -3,6 +3,7 @@
import gettext as gt import gettext as gt
import os import os
import re
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from typing import Any, List from typing import Any, List
@ -53,6 +54,9 @@ class Translator:
Analyse all strings in the project and extract them. Analyse all strings in the project and extract them.
""" """
for language in cls.SUPPORTED_LOCALES: for language in cls.SUPPORTED_LOCALES:
if language == "en":
# Don't translate the main language
continue
file_name = Path(__file__).parent / "locale" / language \ file_name = Path(__file__).parent / "locale" / language \
/ "LC_MESSAGES" / "squirrelbattle.po" / "LC_MESSAGES" / "squirrelbattle.po"
args = ["find", "squirrelbattle", "-iname", "*.py"] args = ["find", "squirrelbattle", "-iname", "*.py"]
@ -65,9 +69,14 @@ class Translator:
"--copyright-holder=ÿnérant, eichhornchen, " "--copyright-holder=ÿnérant, eichhornchen, "
"nicomarg, charlse", "nicomarg, charlse",
"--msgid-bugs-address=squirrel-battle@crans.org", "--msgid-bugs-address=squirrel-battle@crans.org",
"--sort-by-file",
"-o", file_name] "-o", file_name]
if file_name.is_file(): if file_name.is_file():
args.append("--join-existing") 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...") print(f"Make {language} messages...")
subprocess.Popen(args, stdin=find.stdout).wait() subprocess.Popen(args, stdin=find.stdout).wait()
@ -77,6 +86,8 @@ class Translator:
Compile translation messages from source files. Compile translation messages from source files.
""" """
for language in cls.SUPPORTED_LOCALES: for language in cls.SUPPORTED_LOCALES:
if language == "en":
continue
args = ["msgfmt", "--check-format", args = ["msgfmt", "--check-format",
"-o", Path(__file__).parent / "locale" / language "-o", Path(__file__).parent / "locale" / language
/ "LC_MESSAGES" / "squirrelbattle.mo", / "LC_MESSAGES" / "squirrelbattle.mo",