Merge branch 'village' into 'master'

Village

Closes #41, #38, #37, #36, and #18

See merge request ynerant/squirrel-battle!44
This commit is contained in:
eichhornchen 2020-12-11 18:23:10 +01:00
commit 9d3e8a9822
17 changed files with 860 additions and 120 deletions

View File

@ -7,7 +7,7 @@ from squirrelbattle.display.mapdisplay import MapDisplay
from squirrelbattle.display.messagedisplay import MessageDisplay
from squirrelbattle.display.statsdisplay import StatsDisplay
from squirrelbattle.display.menudisplay import MainMenuDisplay, \
InventoryDisplay, SettingsMenuDisplay
PlayerInventoryDisplay, StoreInventoryDisplay, SettingsMenuDisplay
from squirrelbattle.display.logsdisplay import LogsDisplay
from squirrelbattle.display.texturepack import TexturePack
from typing import Any
@ -24,7 +24,8 @@ class DisplayManager:
self.mapdisplay = MapDisplay(screen, pack)
self.statsdisplay = StatsDisplay(screen, pack)
self.logsdisplay = LogsDisplay(screen, pack)
self.inventorydisplay = InventoryDisplay(screen, pack)
self.playerinventorydisplay = PlayerInventoryDisplay(screen, pack)
self.storeinventorydisplay = StoreInventoryDisplay(screen, pack)
self.mainmenudisplay = MainMenuDisplay(self.game.main_menu,
screen, pack)
self.settingsmenudisplay = SettingsMenuDisplay(screen, pack)
@ -33,7 +34,9 @@ class DisplayManager:
self.vbar = VerticalSplit(screen, pack)
self.displays = [self.statsdisplay, self.mapdisplay,
self.mainmenudisplay, self.settingsmenudisplay,
self.logsdisplay, self.messagedisplay]
self.logsdisplay, self.messagedisplay,
self.playerinventorydisplay,
self.storeinventorydisplay]
self.update_game_components()
def handle_display_action(self, action: DisplayActions) -> None:
@ -47,14 +50,18 @@ 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.game.inventory_menu.update_player(self.game.player)
self.game.store_menu.update_merchant(self.game.player)
self.playerinventorydisplay.update_menu(self.game.inventory_menu)
self.storeinventorydisplay.update_menu(self.game.store_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 \
or self.game.state == GameMode.INVENTORY:
or self.game.state == GameMode.INVENTORY \
or self.game.state == GameMode.STORE:
# The map pad has already the good size
self.mapdisplay.refresh(0, 0, self.rows * 4 // 5,
self.mapdisplay.pack.tile_width
@ -68,10 +75,13 @@ class DisplayManager:
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.INVENTORY:
self.inventorydisplay.refresh(self.rows // 10,
self.cols // 2,
8 * self.rows // 10,
2 * self.cols // 5)
self.playerinventorydisplay.refresh(
self.rows // 10, self.cols // 2,
8 * self.rows // 10, 2 * self.cols // 5)
elif self.game.state == GameMode.STORE:
self.storeinventorydisplay.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)
elif self.game.state == GameMode.SETTINGS:

View File

@ -10,6 +10,9 @@ from ..translations import gettext as _
class MenuDisplay(Display):
"""
A class to display the menu objects
"""
position: int
def __init__(self, *args, **kwargs):
@ -63,6 +66,9 @@ class MenuDisplay(Display):
class SettingsMenuDisplay(MenuDisplay):
"""
A class to display specifically a settingsmenu object
"""
@property
def values(self) -> List[str]:
return [_(a[1][1]) + (" : "
@ -73,6 +79,9 @@ class SettingsMenuDisplay(MenuDisplay):
class MainMenuDisplay(Display):
"""
A class to display specifically a mainmenu object
"""
def __init__(self, menu: MainMenu, *args):
super().__init__(*args)
self.menu = menu
@ -100,11 +109,12 @@ class MainMenuDisplay(Display):
self.height - menuy), menuwidth)
class InventoryDisplay(MenuDisplay):
class PlayerInventoryDisplay(MenuDisplay):
message = _("== INVENTORY ==")
def update_pad(self) -> None:
message = _("== INVENTORY ==")
self.addstr(self.pad, 0, (self.width - len(message)) // 2, message,
curses.A_BOLD | curses.A_ITALIC)
self.addstr(self.pad, 0, (self.width - len(self.message)) // 2,
self.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} "
@ -118,3 +128,25 @@ class InventoryDisplay(MenuDisplay):
@property
def trueheight(self) -> int:
return 2 + super().trueheight
class StoreInventoryDisplay(MenuDisplay):
message = _("== STALL ==")
def update_pad(self) -> None:
self.addstr(self.pad, 0, (self.width - len(self.message)) // 2,
self.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()
+ ": " + str(item.price) + " Hazels")
@property
def truewidth(self) -> int:
return max(1, self.height if hasattr(self, "height") else 10)
@property
def trueheight(self) -> int:
return 2 + super().trueheight

View File

@ -46,8 +46,11 @@ class StatsDisplay(Display):
printed_items.append(item)
self.addstr(self.pad, 8, 0, inventory_str)
self.addstr(self.pad, 9, 0, f"{self.pack.HAZELNUT} "
f"x{self.player.hazel}")
if self.player.dead:
self.addstr(self.pad, 10, 0, _("YOU ARE DEAD"),
self.addstr(self.pad, 11, 0, _("YOU ARE DEAD"),
curses.A_BOLD | curses.A_BLINK | curses.A_STANDOUT
| self.color_pair(3))

View File

@ -14,10 +14,22 @@ class TexturePack:
tile_bg_color: int
entity_fg_color: int
entity_bg_color: int
BODY_SNATCH_POTION: str
BOMB: str
HEART: str
HEDGEHOG: str
EMPTY: str
WALL: str
FLOOR: str
HAZELNUT: str
MERCHANT: str
PLAYER: str
RABBIT: str
SUNFLOWER: str
SWORD: str
TEDDY_BEAR: str
TIGER: str
WALL: str
ASCII_PACK: "TexturePack"
SQUIRREL_PACK: "TexturePack"
@ -46,17 +58,22 @@ TexturePack.ASCII_PACK = TexturePack(
tile_bg_color=curses.COLOR_BLACK,
entity_fg_color=curses.COLOR_WHITE,
entity_bg_color=curses.COLOR_BLACK,
EMPTY=' ',
WALL='#',
FLOOR='.',
PLAYER='@',
HEDGEHOG='*',
HEART='',
BOMB='o',
RABBIT='Y',
TIGER='n',
TEDDY_BEAR='8',
BODY_SNATCH_POTION='S',
BOMB='o',
EMPTY=' ',
FLOOR='.',
HAZELNUT='¤',
HEART='',
HEDGEHOG='*',
MERCHANT='M',
PLAYER='@',
RABBIT='Y',
SUNFLOWER='I',
SWORD='\u2020',
TEDDY_BEAR='8',
TIGER='n',
WALL='#',
)
TexturePack.SQUIRREL_PACK = TexturePack(
@ -66,15 +83,20 @@ TexturePack.SQUIRREL_PACK = TexturePack(
tile_bg_color=curses.COLOR_BLACK,
entity_fg_color=curses.COLOR_WHITE,
entity_bg_color=curses.COLOR_WHITE,
EMPTY=' ',
WALL='🧱',
FLOOR='██',
PLAYER='🐿️ ',
HEDGEHOG='🦔',
HEART='💜',
BOMB='💣',
RABBIT='🐇',
TIGER='🐅',
TEDDY_BEAR='🧸',
BODY_SNATCH_POTION='🔀',
BOMB='💣',
EMPTY=' ',
FLOOR='██',
HAZELNUT='🌰',
HEART='💜',
HEDGEHOG='🦔',
PLAYER='🐿️ ',
MERCHANT='🦜',
RABBIT='🐇',
SUNFLOWER='🌻',
SWORD='🗡️',
TEDDY_BEAR='🧸',
TIGER='🐅',
WALL='🧱',
)

View File

@ -0,0 +1,50 @@
from ..interfaces import FriendlyEntity, InventoryHolder
from ..translations import gettext as _
from .player import Player
from .items import Item
from random import choice
class Merchant(InventoryHolder, FriendlyEntity):
"""
The class for merchants in the dungeon
"""
def keys(self) -> list:
"""
Returns a friendly entitie's specific attributes
"""
return super().keys() + ["inventory", "hazel"]
def __init__(self, name: str = "merchant", inventory: list = None,
hazel: int = 75, *args, **kwargs):
super().__init__(name=name, *args, **kwargs)
self.inventory = self.translate_inventory(inventory or [])
self.hazel = hazel
if not self.inventory:
for i in range(5):
self.inventory.append(choice(Item.get_all_items())())
def talk_to(self, player: Player) -> str:
"""
This function is used to open the merchant's inventory in a menu,
and allow the player to buy/sell objects
"""
return _("I don't sell any squirrel")
def change_hazel_balance(self, hz: int) -> None:
"""
Change the number of hazel the merchant has by hz.
"""
self.hazel += hz
class Sunflower(FriendlyEntity):
"""
A friendly sunflower
"""
dialogue_option = [_("Flower power!!"), _("The sun is warm today")]
def __init__(self, maxhealth: int = 15,
*args, **kwargs) -> None:
super().__init__(name="sunflower", maxhealth=maxhealth, *args, **kwargs)

View File

@ -5,7 +5,7 @@ from random import choice, randint
from typing import Optional
from .player import Player
from ..interfaces import Entity, FightingEntity, Map
from ..interfaces import Entity, FightingEntity, Map, InventoryHolder
from ..translations import gettext as _
@ -14,13 +14,16 @@ class Item(Entity):
A class for items
"""
held: bool
held_by: Optional[Player]
held_by: Optional[InventoryHolder]
price: int
def __init__(self, held: bool = False, held_by: Optional[Player] = None,
*args, **kwargs):
def __init__(self, held: bool = False,
held_by: Optional[InventoryHolder] = None,
price: int = 2, *args, **kwargs):
super().__init__(*args, **kwargs)
self.held = held
self.held_by = held_by
self.price = price
def drop(self) -> None:
"""
@ -43,14 +46,14 @@ class Item(Entity):
Indicates what should be done when the item is equipped.
"""
def hold(self, player: "Player") -> None:
def hold(self, player: InventoryHolder) -> None:
"""
The item is taken from the floor and put into the inventory
"""
self.held = True
self.held_by = player
self.map.remove_entity(self)
player.inventory.append(self)
self.held_by.map.remove_entity(self)
player.add_to_inventory(self)
def save_state(self) -> dict:
"""
@ -60,6 +63,25 @@ class Item(Entity):
d["held"] = self.held
return d
@staticmethod
def get_all_items() -> list:
return [BodySnatchPotion, Bomb, Heart, Sword]
def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool:
"""
Does all necessary actions when an object is to be sold.
Is overwritten by some classes that cannot exist in the player's
inventory
"""
if buyer.hazel >= self.price:
self.hold(buyer)
seller.remove_from_inventory(self)
buyer.change_hazel_balance(-self.price)
seller.change_hazel_balance(self.price)
return True
else:
return False
class Heart(Item):
"""
@ -67,16 +89,17 @@ class Heart(Item):
"""
healing: int
def __init__(self, name: str = "heart", healing: int = 5, *args, **kwargs):
super().__init__(name=name, *args, **kwargs)
def __init__(self, name: str = "heart", healing: int = 5, price: int = 3,
*args, **kwargs):
super().__init__(name=name, price=price, *args, **kwargs)
self.healing = healing
def hold(self, player: "Player") -> None:
def hold(self, entity: InventoryHolder) -> None:
"""
When holding a heart, heal the player and don't put item in inventory.
"""
player.health = min(player.maxhealth, player.health + self.healing)
self.map.remove_entity(self)
entity.health = min(entity.maxhealth, entity.health + self.healing)
entity.map.remove_entity(self)
def save_state(self) -> dict:
"""
@ -97,8 +120,8 @@ class Bomb(Item):
tick: int
def __init__(self, name: str = "bomb", damage: int = 5,
exploding: bool = False, *args, **kwargs):
super().__init__(name=name, *args, **kwargs)
exploding: bool = False, price: int = 4, *args, **kwargs):
super().__init__(name=name, price=price, *args, **kwargs)
self.damage = damage
self.exploding = exploding
self.tick = 4
@ -145,14 +168,43 @@ class Bomb(Item):
return d
class Weapon(Item):
"""
Non-throwable items that improve player damage
"""
damage: int
def __init__(self, damage: int = 3, *args, **kwargs):
super().__init__(*args, **kwargs)
self.damage = damage
def save_state(self) -> dict:
"""
Saves the state of the weapon into a dictionary
"""
d = super().save_state()
d["damage"] = self.damage
return d
class Sword(Weapon):
"""
A basic weapon
"""
def __init__(self, name: str = "sword", price: int = 20, *args, **kwargs):
super().__init__(name=name, price=price, *args, **kwargs)
self.name = name
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 __init__(self, name: str = "body_snatch_potion", price: int = 14,
*args, **kwargs):
super().__init__(name=name, price=price, *args, **kwargs)
def use(self) -> None:
"""

View File

@ -6,23 +6,22 @@ from queue import PriorityQueue
from random import randint
from typing import Dict, Tuple
from ..interfaces import FightingEntity
from ..interfaces import FightingEntity, InventoryHolder
class Player(FightingEntity):
class Player(InventoryHolder, FightingEntity):
"""
The class of the player
"""
current_xp: int = 0
max_xp: int = 10
inventory: list
paths: Dict[Tuple[int, int], Tuple[int, int]]
def __init__(self, name: str = "player", maxhealth: int = 20,
strength: int = 5, intelligence: int = 1, charisma: int = 1,
dexterity: int = 1, constitution: int = 1, level: int = 1,
current_xp: int = 0, max_xp: int = 10, inventory: list = None,
*args, **kwargs) \
hazel: int = 42, *args, **kwargs) \
-> None:
super().__init__(name=name, maxhealth=maxhealth, strength=strength,
intelligence=intelligence, charisma=charisma,
@ -30,13 +29,9 @@ class Player(FightingEntity):
level=level, *args, **kwargs)
self.current_xp = current_xp
self.max_xp = max_xp
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.inventory = self.translate_inventory(inventory or [])
self.paths = dict()
self.hazel = hazel
def move(self, y: int, x: int) -> None:
"""
@ -149,5 +144,4 @@ class Player(FightingEntity):
d = super().save_state()
d["current_xp"] = self.current_xp
d["max_xp"] = self.max_xp
d["inventory"] = [item.save_state() for item in self.inventory]
return d

View File

@ -26,6 +26,7 @@ class GameMode(Enum):
PLAY = auto()
SETTINGS = auto()
INVENTORY = auto()
STORE = auto()
class KeyValues(Enum):
@ -42,6 +43,7 @@ class KeyValues(Enum):
EQUIP = auto()
DROP = auto()
SPACE = auto()
CHAT = auto()
@staticmethod
def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]:
@ -72,4 +74,6 @@ class KeyValues(Enum):
return KeyValues.DROP
elif key == ' ':
return KeyValues.SPACE
elif key == settings.KEY_CHAT:
return KeyValues.CHAT
return None

View File

@ -24,6 +24,7 @@ class Game:
"""
map: Map
player: Player
screen: Any
# display_actions is a display interface set by the bootstrapper
display_actions: Callable[[DisplayActions], None]
@ -32,6 +33,7 @@ class Game:
Init the game.
"""
self.state = GameMode.MAINMENU
self.waiting_for_friendly_key = False
self.settings = Settings()
self.settings.load_settings()
self.settings.write_settings()
@ -40,6 +42,7 @@ class Game:
self.settings_menu = menus.SettingsMenu()
self.settings_menu.update_values(self.settings)
self.inventory_menu = menus.InventoryMenu()
self.store_menu = menus.StoreMenu()
self.logs = Logs()
self.message = None
@ -83,13 +86,19 @@ class Game:
return
if self.state == GameMode.PLAY:
self.handle_key_pressed_play(key)
if self.waiting_for_friendly_key:
# The player requested to talk with a friendly entity
self.handle_friendly_entity_chat(key)
else:
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:
self.settings_menu.handle_key_pressed(key, raw_key, self)
elif self.state == GameMode.STORE:
self.handle_key_pressed_store(key)
self.display_actions(DisplayActions.REFRESH)
def handle_key_pressed_play(self, key: KeyValues) -> None:
@ -112,6 +121,42 @@ class Game:
self.state = GameMode.INVENTORY
elif key == KeyValues.SPACE:
self.state = GameMode.MAINMENU
elif key == KeyValues.CHAT:
# Wait for the direction of the friendly entity
self.waiting_for_friendly_key = True
def handle_friendly_entity_chat(self, key: KeyValues) -> None:
"""
If the player is talking to a friendly entity, we get the direction
where the entity is, then we interact with it.
"""
if not self.waiting_for_friendly_key:
return
self.waiting_for_friendly_key = False
if key == KeyValues.UP:
xp = self.player.x
yp = self.player.y - 1
elif key == KeyValues.DOWN:
xp = self.player.x
yp = self.player.y + 1
elif key == KeyValues.LEFT:
xp = self.player.x - 1
yp = self.player.y
elif key == KeyValues.RIGHT:
xp = self.player.x + 1
yp = self.player.y
else:
return
if self.map.entity_is_present(yp, xp):
for entity in self.map.entities:
if entity.is_friendly() and entity.x == xp and \
entity.y == yp:
msg = entity.talk_to(self.player)
self.logs.add_message(msg)
if entity.is_merchant():
self.state = GameMode.STORE
self.store_menu.update_merchant(entity)
def handle_key_pressed_inventory(self, key: KeyValues) -> None:
"""
@ -136,6 +181,27 @@ class Game:
len(self.inventory_menu.values)
- 1)
def handle_key_pressed_store(self, key: KeyValues) -> None:
"""
In a store menu, we can buy items or close the menu.
"""
if key == KeyValues.SPACE:
self.state = GameMode.PLAY
elif key == KeyValues.UP:
self.store_menu.go_up()
elif key == KeyValues.DOWN:
self.store_menu.go_down()
if self.store_menu.values and not self.player.dead:
if key == KeyValues.ENTER:
item = self.store_menu.validate()
flag = item.be_sold(self.player, self.store_menu.merchant)
if not flag:
self.message = _("You do not have enough money")
self.display_actions(DisplayActions.UPDATE)
# Ensure that the cursor has a good position
self.store_menu.position = min(self.store_menu.position,
len(self.store_menu.values) - 1)
def handle_key_pressed_main_menu(self, key: KeyValues) -> None:
"""
In the main menu, we can navigate through options.

View File

@ -4,7 +4,7 @@
from enum import Enum, auto
from math import sqrt
from random import choice, randint
from typing import List, Optional
from typing import List, Optional, Any
from .display.texturepack import TexturePack
from .translations import gettext as _
@ -68,7 +68,8 @@ class Map:
"""
Unregister an entity from the map.
"""
self.entities.remove(entity)
if entity in self.entities:
self.entities.remove(entity)
def find_entities(self, entity_class: type) -> list:
return [entity for entity in self.entities
@ -76,12 +77,21 @@ class Map:
def is_free(self, y: int, x: int) -> bool:
"""
Indicates that the case at the coordinates (y, x) is empty.
Indicates that the tile at the coordinates (y, x) is empty.
"""
return 0 <= y < self.height and 0 <= x < self.width and \
self.tiles[y][x].can_walk() and \
not any(entity.x == x and entity.y == y for entity in self.entities)
def entity_is_present(self, y: int, x: int) -> bool:
"""
Indicates that the tile at the coordinates (y, x) contains a killable
entity
"""
return 0 <= y < self.height and 0 <= x < self.width and \
any(entity.x == x and entity.y == y and entity.is_friendly()
for entity in self.entities)
@staticmethod
def load(filename: str) -> "Map":
"""
@ -127,7 +137,7 @@ class Map:
def spawn_random_entities(self, count: int) -> None:
"""
Put randomly {count} hedgehogs on the map, where it is available.
Put randomly {count} entities on the map, where it is available.
"""
for ignored in range(count):
y, x = 0, 0
@ -315,20 +325,34 @@ class Entity:
from squirrelbattle.entities.items import Item
return isinstance(self, Item)
def is_friendly(self) -> bool:
"""
Is this entity a friendly entity?
"""
return isinstance(self, FriendlyEntity)
def is_merchant(self) -> bool:
"""
Is this entity a merchant?
"""
from squirrelbattle.entities.friendly import Merchant
return isinstance(self, Merchant)
@property
def translated_name(self) -> str:
return _(self.name.replace("_", " "))
@staticmethod
def get_all_entity_classes():
def get_all_entity_classes() -> list:
"""
Returns all entities subclasses
"""
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart
from squirrelbattle.entities.monsters import Tiger, Hedgehog, \
Rabbit, TeddyBear
return [BodySnatchPotion, Bomb, Heart, Hedgehog,
Rabbit, TeddyBear, Tiger]
from squirrelbattle.entities.friendly import Merchant, Sunflower
return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear,
Sunflower, Tiger, Merchant]
@staticmethod
def get_all_entity_classes_in_a_dict() -> dict:
@ -338,7 +362,9 @@ class Entity:
from squirrelbattle.entities.player import Player
from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \
TeddyBear
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart
from squirrelbattle.entities.friendly import Merchant, Sunflower
from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \
Heart, Sword
return {
"Tiger": Tiger,
"Bomb": Bomb,
@ -348,6 +374,9 @@ class Entity:
"Rabbit": Rabbit,
"TeddyBear": TeddyBear,
"Player": Player,
"Merchant": Merchant,
"Sunflower": Sunflower,
"Sword": Sword,
}
def save_state(self) -> dict:
@ -423,7 +452,7 @@ class FightingEntity(Entity):
def keys(self) -> list:
"""
Returns a fighting entities specific attributes
Returns a fighting entity's specific attributes
"""
return ["name", "maxhealth", "health", "level", "strength",
"intelligence", "charisma", "dexterity", "constitution"]
@ -436,3 +465,74 @@ class FightingEntity(Entity):
for name in self.keys():
d[name] = getattr(self, name)
return d
class FriendlyEntity(FightingEntity):
"""
Friendly entities are living entities which do not attack the player
"""
dialogue_option: list
def talk_to(self, player: Any) -> str:
a = randint(0, len(self.dialogue_option) - 1)
return "The " + self.translated_name \
+ " said : " + self.dialogue_option[a]
def keys(self) -> list:
"""
Returns a friendly entity's specific attributes
"""
return ["maxhealth", "health"]
class InventoryHolder(Entity):
hazel: int # Currency of the game
inventory: list
def translate_inventory(self, inventory: list) -> list:
"""
Translate the JSON-state of the inventory into a list of the items in
the inventory.
"""
for i in range(len(inventory)):
if isinstance(inventory[i], dict):
inventory[i] = self.dict_to_inventory(inventory[i])
return inventory
def dict_to_inventory(self, item_dict: dict) -> Entity:
"""
Translate a dict object that contains the state of an item
into an item object.
"""
entity_classes = self.get_all_entity_classes_in_a_dict()
item_class = entity_classes[item_dict["type"]]
return item_class(**item_dict)
def save_state(self) -> dict:
"""
We save the inventory of the merchant formatted as JSON
"""
d = super().save_state()
d["hazel"] = self.hazel
d["inventory"] = [item.save_state() for item in self.inventory]
return d
def add_to_inventory(self, obj: Any) -> None:
"""
Adds an object to inventory
"""
self.inventory.append(obj)
def remove_from_inventory(self, obj: Any) -> None:
"""
Removes an object from the inventory
"""
self.inventory.remove(obj)
def change_hazel_balance(self, hz: int) -> None:
"""
Change the number of hazel the entity has by hz. hz is negative
when the player loses money and positive when he gains money
"""
self.hazel += hz

View File

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: squirrelbattle 3.14.1\n"
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
"POT-Creation-Date: 2020-12-05 14:46+0100\n"
"POT-Creation-Date: 2020-12-11 18:06+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,31 +15,52 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: squirrelbattle/display/menudisplay.py:105
#: squirrelbattle/display/menudisplay.py:113
msgid "== INVENTORY =="
msgstr "== BESTAND =="
#: squirrelbattle/display/menudisplay.py:134
msgid "== STALL =="
msgstr "== STAND =="
#: squirrelbattle/display/statsdisplay.py:34
msgid "Inventory:"
msgstr "Bestand:"
#: squirrelbattle/display/statsdisplay.py:50
#: squirrelbattle/display/statsdisplay.py:53
msgid "YOU ARE DEAD"
msgstr "SIE WURDEN GESTORBEN"
#. TODO
#: squirrelbattle/entities/friendly.py:33
msgid "I don't sell any squirrel"
msgstr "Ich verkaufe keinen Eichhörnchen."
#: squirrelbattle/entities/friendly.py:46
msgid "Flower power!!"
msgstr "Blumenmacht!!"
#: squirrelbattle/entities/friendly.py:46
msgid "The sun is warm today"
msgstr "Die Sonne ist warm heute"
#. The bomb is exploding.
#. Each entity that is close to the bomb takes damages.
#. The player earn XP if the entity was killed.
#: squirrelbattle/entities/items.py:128
#: squirrelbattle/entities/items.py:151
msgid "Bomb is exploding."
msgstr "Die Bombe explodiert."
#: squirrelbattle/entities/items.py:172
#: squirrelbattle/entities/items.py:224
#, python-brace-format
msgid "{player} exchanged its body with {entity}."
msgstr "{player} täuscht seinem Körper mit {entity} aus."
#: squirrelbattle/game.py:177
#: squirrelbattle/game.py:199 squirrelbattle/tests/game_test.py:537
msgid "You do not have enough money"
msgstr ""
#: squirrelbattle/game.py:243
msgid ""
"Some keys are missing in your save file.\n"
"Your save seems to be corrupt. It got deleted."
@ -47,7 +68,7 @@ msgstr ""
"In Ihrer Speicherdatei fehlen einige Schlüssel.\n"
"Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht."
#: squirrelbattle/game.py:185
#: squirrelbattle/game.py:251
msgid ""
"No player was found on this map!\n"
"Maybe you died?"
@ -55,7 +76,7 @@ msgstr ""
"Auf dieser Karte wurde kein Spieler gefunden!\n"
"Vielleicht sind Sie gestorben?"
#: squirrelbattle/game.py:205
#: squirrelbattle/game.py:271
msgid ""
"The JSON file is not correct.\n"
"Your save seems corrupted. It got deleted."
@ -63,27 +84,27 @@ msgstr ""
"Die JSON-Datei ist nicht korrekt.\n"
"Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht."
#: squirrelbattle/interfaces.py:400
#: squirrelbattle/interfaces.py:429
#, python-brace-format
msgid "{name} hits {opponent}."
msgstr "{name} schlägt {opponent}."
#: squirrelbattle/interfaces.py:412
#: squirrelbattle/interfaces.py:441
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} nimmt {amount} Schadenspunkte."
#: squirrelbattle/interfaces.py:414
#: squirrelbattle/interfaces.py:443
#, python-brace-format
msgid "{name} dies."
msgstr "{name} stirbt."
#: squirrelbattle/menus.py:72
#: squirrelbattle/menus.py:73
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/game_test.py:314 squirrelbattle/tests/game_test.py:317
#: squirrelbattle/tests/game_test.py:320
#: squirrelbattle/tests/translations_test.py:16
msgid "New game"
msgstr "Neu Spiel"
@ -161,41 +182,49 @@ 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 "Key used to talk to a friendly entity"
msgstr "Taste um mit einer friedlicher Entität zu sprechen"
#: squirrelbattle/tests/translations_test.py:55
msgid "Texture pack"
msgstr "Textur-Packung"
#: squirrelbattle/tests/translations_test.py:54
#: squirrelbattle/tests/translations_test.py:56
msgid "Language"
msgstr "Sprache"
#: squirrelbattle/tests/translations_test.py:57
#: squirrelbattle/tests/translations_test.py:59
msgid "player"
msgstr "Spieler"
#: squirrelbattle/tests/translations_test.py:59
#: squirrelbattle/tests/translations_test.py:61
msgid "tiger"
msgstr "Tiger"
#: squirrelbattle/tests/translations_test.py:60
#: squirrelbattle/tests/translations_test.py:62
msgid "hedgehog"
msgstr "Igel"
#: squirrelbattle/tests/translations_test.py:61
#: squirrelbattle/tests/translations_test.py:63
msgid "rabbit"
msgstr "Kanninchen"
#: squirrelbattle/tests/translations_test.py:62
#: squirrelbattle/tests/translations_test.py:64
msgid "teddy bear"
msgstr "Teddybär"
#: squirrelbattle/tests/translations_test.py:64
#: squirrelbattle/tests/translations_test.py:66
msgid "body snatch potion"
msgstr "Leichenfleddererzaubertrank"
#: squirrelbattle/tests/translations_test.py:65
#: squirrelbattle/tests/translations_test.py:67
msgid "bomb"
msgstr "Bombe"
#: squirrelbattle/tests/translations_test.py:66
#: squirrelbattle/tests/translations_test.py:68
msgid "heart"
msgstr "Herz"
#: squirrelbattle/tests/translations_test.py:69
msgid "sword"
msgstr "schwert"

View File

@ -0,0 +1,207 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse
# This file is distributed under the same license as the squirrelbattle package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: squirrelbattle 3.14.1\n"
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
"POT-Creation-Date: 2020-12-01 17:10+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: squirrelbattle/display/statsdisplay.py:34
msgid "Inventory:"
msgstr ""
#: squirrelbattle/display/statsdisplay.py:39
msgid "YOU ARE DEAD"
msgstr ""
#: squirrelbattle/interfaces.py:394 squirrelbattle/interfaces.py:398
#: squirrelbattle/interfaces.py:408
#, python-brace-format
msgid "{name} hits {opponent}."
msgstr ""
#: squirrelbattle/interfaces.py:405 squirrelbattle/interfaces.py:410
#: squirrelbattle/interfaces.py:420
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr ""
#: 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 ""
#: squirrelbattle/menus.py:46 squirrelbattle/tests/translations_test.py:15
#: squirrelbattle/tests/translations_test.py:17
msgid "Resume"
msgstr ""
#: squirrelbattle/menus.py:47 squirrelbattle/tests/translations_test.py:17
#: squirrelbattle/tests/translations_test.py:19
msgid "Save"
msgstr ""
#: squirrelbattle/menus.py:48 squirrelbattle/tests/translations_test.py:16
#: squirrelbattle/tests/translations_test.py:18
msgid "Load"
msgstr ""
#: squirrelbattle/menus.py:49 squirrelbattle/tests/translations_test.py:18
#: squirrelbattle/tests/translations_test.py:20
msgid "Settings"
msgstr ""
#: squirrelbattle/menus.py:50 squirrelbattle/tests/translations_test.py:19
#: squirrelbattle/tests/translations_test.py:21
msgid "Exit"
msgstr ""
#: squirrelbattle/menus.py:71
msgid "Back"
msgstr ""
#: squirrelbattle/game.py:147 squirrelbattle/game.py:148
msgid ""
"Some keys are missing in your save file.\n"
"Your save seems to be corrupt. It got deleted."
msgstr ""
#: squirrelbattle/game.py:155 squirrelbattle/game.py:156
msgid ""
"No player was found on this map!\n"
"Maybe you died?"
msgstr ""
#: squirrelbattle/game.py:175 squirrelbattle/game.py:176
msgid ""
"The JSON file is not correct.\n"
"Your save seems corrupted. It got deleted."
msgstr ""
#: squirrelbattle/settings.py:21 squirrelbattle/tests/translations_test.py:21
#: squirrelbattle/tests/translations_test.py:25
#: squirrelbattle/tests/translations_test.py:27
msgid "Main key to move up"
msgstr ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: 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 ""
#: squirrelbattle/settings.py:31 squirrelbattle/tests/translations_test.py:39
#: squirrelbattle/tests/translations_test.py:43
#: squirrelbattle/tests/translations_test.py:45
msgid "Texture pack"
msgstr ""
#: squirrelbattle/settings.py:32 squirrelbattle/tests/translations_test.py:40
#: squirrelbattle/tests/translations_test.py:44
#: squirrelbattle/tests/translations_test.py:46
msgid "Language"
msgstr ""
#: squirrelbattle/interfaces.py:407 squirrelbattle/interfaces.py:412
#: squirrelbattle/interfaces.py:422
#, python-brace-format
msgid "{name} dies."
msgstr ""
#: squirrelbattle/tests/translations_test.py:47
#: squirrelbattle/tests/translations_test.py:49
msgid "player"
msgstr ""
#: squirrelbattle/tests/translations_test.py:49
#: squirrelbattle/tests/translations_test.py:51
msgid "tiger"
msgstr ""
#: squirrelbattle/tests/translations_test.py:50
#: squirrelbattle/tests/translations_test.py:52
msgid "hedgehog"
msgstr ""
#: squirrelbattle/tests/translations_test.py:51
#: squirrelbattle/tests/translations_test.py:53
msgid "rabbit"
msgstr ""
#: squirrelbattle/tests/translations_test.py:52
#: squirrelbattle/tests/translations_test.py:54
msgid "teddy bear"
msgstr ""
#: squirrelbattle/tests/translations_test.py:54
#: squirrelbattle/tests/translations_test.py:56
msgid "bomb"
msgstr ""
#: squirrelbattle/tests/translations_test.py:55
#: squirrelbattle/tests/translations_test.py:57
msgid "heart"
msgstr ""
#: squirrelbattle/entities/friendly.py:31
msgid "Flower power!!"
msgstr ""
#: squirrelbattle/entities/friendly.py:31
msgid "The sun is warm today"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: squirrelbattle 3.14.1\n"
"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n"
"POT-Creation-Date: 2020-12-05 14:46+0100\n"
"POT-Creation-Date: 2020-12-11 18:06+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -16,31 +16,52 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: squirrelbattle/display/menudisplay.py:105
#: squirrelbattle/display/menudisplay.py:113
msgid "== INVENTORY =="
msgstr "== INVENTAIRE =="
#: squirrelbattle/display/menudisplay.py:134
msgid "== STALL =="
msgstr "== STAND =="
#: squirrelbattle/display/statsdisplay.py:34
msgid "Inventory:"
msgstr "Inventaire :"
#: squirrelbattle/display/statsdisplay.py:50
#: squirrelbattle/display/statsdisplay.py:53
msgid "YOU ARE DEAD"
msgstr "VOUS ÊTES MORT"
#. TODO
#: squirrelbattle/entities/friendly.py:33
msgid "I don't sell any squirrel"
msgstr "Je ne vends pas d'écureuil"
#: squirrelbattle/entities/friendly.py:46
msgid "Flower power!!"
msgstr "Pouvoir des fleurs !"
#: squirrelbattle/entities/friendly.py:46
msgid "The sun is warm today"
msgstr "Le soleil est chaud aujourd'hui"
#. The bomb is exploding.
#. Each entity that is close to the bomb takes damages.
#. The player earn XP if the entity was killed.
#: squirrelbattle/entities/items.py:128
#: squirrelbattle/entities/items.py:151
msgid "Bomb is exploding."
msgstr "La bombe explose."
#: squirrelbattle/entities/items.py:172
#: squirrelbattle/entities/items.py:224
#, python-brace-format
msgid "{player} exchanged its body with {entity}."
msgstr "{player} a échangé son corps avec {entity}."
#: squirrelbattle/game.py:177
#: squirrelbattle/game.py:199 squirrelbattle/tests/game_test.py:537
msgid "You do not have enough money"
msgstr ""
#: squirrelbattle/game.py:243
msgid ""
"Some keys are missing in your save file.\n"
"Your save seems to be corrupt. It got deleted."
@ -48,7 +69,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:185
#: squirrelbattle/game.py:251
msgid ""
"No player was found on this map!\n"
"Maybe you died?"
@ -56,7 +77,7 @@ msgstr ""
"Aucun joueur n'a été trouvé sur la carte !\n"
"Peut-être êtes-vous mort ?"
#: squirrelbattle/game.py:205
#: squirrelbattle/game.py:271
msgid ""
"The JSON file is not correct.\n"
"Your save seems corrupted. It got deleted."
@ -64,27 +85,27 @@ msgstr ""
"Le fichier JSON de sauvegarde est incorrect.\n"
"Votre sauvegarde semble corrompue. Elle a été supprimée."
#: squirrelbattle/interfaces.py:400
#: squirrelbattle/interfaces.py:429
#, python-brace-format
msgid "{name} hits {opponent}."
msgstr "{name} frappe {opponent}."
#: squirrelbattle/interfaces.py:412
#: squirrelbattle/interfaces.py:441
#, python-brace-format
msgid "{name} takes {amount} damage."
msgstr "{name} prend {amount} points de dégât."
#: squirrelbattle/interfaces.py:414
#: squirrelbattle/interfaces.py:443
#, python-brace-format
msgid "{name} dies."
msgstr "{name} meurt."
#: squirrelbattle/menus.py:72
#: squirrelbattle/menus.py:73
msgid "Back"
msgstr "Retour"
#: squirrelbattle/tests/game_test.py:300 squirrelbattle/tests/game_test.py:303
#: squirrelbattle/tests/game_test.py:306
#: squirrelbattle/tests/game_test.py:314 squirrelbattle/tests/game_test.py:317
#: squirrelbattle/tests/game_test.py:320
#: squirrelbattle/tests/translations_test.py:16
msgid "New game"
msgstr "Nouvelle partie"
@ -162,41 +183,49 @@ 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 "Key used to talk to a friendly entity"
msgstr "Touche pour parler à une entité pacifique"
#: squirrelbattle/tests/translations_test.py:55
msgid "Texture pack"
msgstr "Pack de textures"
#: squirrelbattle/tests/translations_test.py:54
#: squirrelbattle/tests/translations_test.py:56
msgid "Language"
msgstr "Langue"
#: squirrelbattle/tests/translations_test.py:57
#: squirrelbattle/tests/translations_test.py:59
msgid "player"
msgstr "joueur"
#: squirrelbattle/tests/translations_test.py:59
#: squirrelbattle/tests/translations_test.py:61
msgid "tiger"
msgstr "tigre"
#: squirrelbattle/tests/translations_test.py:60
#: squirrelbattle/tests/translations_test.py:62
msgid "hedgehog"
msgstr "hérisson"
#: squirrelbattle/tests/translations_test.py:61
#: squirrelbattle/tests/translations_test.py:63
msgid "rabbit"
msgstr "lapin"
#: squirrelbattle/tests/translations_test.py:62
#: squirrelbattle/tests/translations_test.py:64
msgid "teddy bear"
msgstr "nounours"
#: squirrelbattle/tests/translations_test.py:64
#: squirrelbattle/tests/translations_test.py:66
msgid "body snatch potion"
msgstr "potion d'arrachage de corps"
#: squirrelbattle/tests/translations_test.py:65
#: squirrelbattle/tests/translations_test.py:67
msgid "bomb"
msgstr "bombe"
#: squirrelbattle/tests/translations_test.py:66
#: squirrelbattle/tests/translations_test.py:68
msgid "heart"
msgstr "cœur"
#: squirrelbattle/tests/translations_test.py:69
msgid "sword"
msgstr "épée"

View File

@ -6,6 +6,7 @@ from typing import Any, Optional
from .display.texturepack import TexturePack
from .entities.player import Player
from .entities.friendly import Merchant
from .enums import GameMode, KeyValues, DisplayActions
from .settings import Settings
from .translations import gettext as _, Translator
@ -128,3 +129,14 @@ class InventoryMenu(Menu):
@property
def values(self) -> list:
return self.player.inventory
class StoreMenu(Menu):
merchant: Merchant
def update_merchant(self, merchant: Merchant) -> None:
self.merchant = merchant
@property
def values(self) -> list:
return self.merchant.inventory

View File

@ -31,6 +31,7 @@ class Settings:
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.KEY_CHAT = ['t', 'Key used to talk to a friendly entity']
self.TEXTURE_PACK = ['ascii', 'Texture pack']
self.LOCALE = [locale.getlocale()[0][:2], 'Language']

View File

@ -7,7 +7,8 @@ import unittest
from ..bootstrap import Bootstrap
from ..display.display import Display
from ..display.display_manager import DisplayManager
from ..entities.items import Bomb
from ..entities.friendly import Merchant, Sunflower
from ..entities.items import Bomb, Heart, Sword
from ..entities.player import Player
from ..enums import DisplayActions
from ..game import Game, KeyValues, GameMode
@ -34,7 +35,17 @@ class TestGame(unittest.TestCase):
"""
bomb = Bomb()
self.game.map.add_entity(bomb)
sword = Sword()
self.game.map.add_entity(sword)
# Add items in the inventory to check that it is well loaded
bomb.hold(self.game.player)
sword.hold(self.game.player)
# Ensure that merchants can be saved
merchant = Merchant()
merchant.move(3, 6)
self.game.map.add_entity(merchant)
old_state = self.game.save_state()
self.game.handle_key_pressed(KeyValues.DOWN)
@ -117,6 +128,9 @@ class TestGame(unittest.TestCase):
self.assertEqual(KeyValues.translate_key(
self.game.settings.KEY_INVENTORY, self.game.settings),
KeyValues.INVENTORY)
self.assertEqual(KeyValues.translate_key(
self.game.settings.KEY_CHAT, self.game.settings),
KeyValues.CHAT)
self.assertEqual(KeyValues.translate_key(
self.game.settings.KEY_USE, self.game.settings),
KeyValues.USE)
@ -280,7 +294,7 @@ class TestGame(unittest.TestCase):
self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a')
# Navigate to "texture pack"
for ignored in range(9):
for ignored in range(10):
self.game.handle_key_pressed(KeyValues.DOWN)
# Change texture pack
@ -417,3 +431,115 @@ class TestGame(unittest.TestCase):
self.assertTrue(bomb.exploding)
self.assertEqual(bomb.y, self.game.player.y)
self.assertEqual(bomb.x, self.game.player.x)
def test_talk_to_sunflowers(self) -> None:
"""
Interact with sunflowers
"""
self.game.state = GameMode.PLAY
sunflower = Sunflower()
sunflower.move(2, 6)
self.game.map.add_entity(sunflower)
# Does nothing
self.assertIsNone(self.game.handle_friendly_entity_chat(KeyValues.UP))
# Talk to sunflower... or not
self.game.handle_key_pressed(KeyValues.CHAT)
self.assertTrue(self.game.waiting_for_friendly_key)
# Wrong key
self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertFalse(self.game.waiting_for_friendly_key)
self.game.handle_key_pressed(KeyValues.CHAT)
self.assertTrue(self.game.waiting_for_friendly_key)
self.game.handle_key_pressed(KeyValues.UP)
self.assertFalse(self.game.waiting_for_friendly_key)
self.assertEqual(self.game.state, GameMode.PLAY)
self.assertFalse(len(self.game.logs.messages) > 1)
# Talk to sunflower
self.game.handle_key_pressed(KeyValues.CHAT)
self.assertTrue(self.game.waiting_for_friendly_key)
self.game.handle_key_pressed(KeyValues.DOWN)
self.assertFalse(self.game.waiting_for_friendly_key)
self.assertEqual(self.game.state, GameMode.PLAY)
self.assertTrue(self.game.logs.messages)
# Ensure that the message is a good message
self.assertIn(self.game.logs.messages[1][21:],
Sunflower.dialogue_option)
# Test all directions to detect the friendly entity
self.game.player.move(3, 6)
self.game.handle_key_pressed(KeyValues.CHAT)
self.game.handle_key_pressed(KeyValues.UP)
self.assertEqual(len(self.game.logs.messages), 3)
self.game.player.move(2, 7)
self.game.handle_key_pressed(KeyValues.CHAT)
self.game.handle_key_pressed(KeyValues.LEFT)
self.assertEqual(len(self.game.logs.messages), 4)
self.game.player.move(2, 5)
self.game.handle_key_pressed(KeyValues.CHAT)
self.game.handle_key_pressed(KeyValues.RIGHT)
self.assertEqual(len(self.game.logs.messages), 5)
def test_talk_to_merchant(self) -> None:
"""
Interact with merchants
"""
self.game.state = GameMode.PLAY
merchant = Merchant()
merchant.move(2, 6)
self.game.map.add_entity(merchant)
# Does nothing
self.assertIsNone(self.game.handle_friendly_entity_chat(KeyValues.UP))
# Talk to merchant
self.game.handle_key_pressed(KeyValues.CHAT)
self.assertTrue(self.game.waiting_for_friendly_key)
self.game.handle_key_pressed(KeyValues.DOWN)
self.assertFalse(self.game.waiting_for_friendly_key)
self.assertEqual(self.game.state, GameMode.STORE)
self.assertTrue(self.game.logs.messages)
# Navigate in the menu
self.game.handle_key_pressed(KeyValues.DOWN)
self.game.handle_key_pressed(KeyValues.DOWN)
self.game.handle_key_pressed(KeyValues.UP)
self.assertEqual(self.game.store_menu.position, 1)
# The second item is not a heart
merchant.inventory[1] = Sword()
# Buy the second item
item = self.game.store_menu.validate()
self.assertIn(item, merchant.inventory)
self.game.handle_key_pressed(KeyValues.ENTER)
self.assertIn(item, self.game.player.inventory)
self.assertNotIn(item, merchant.inventory)
# Buy a heart
merchant.inventory[1] = Heart()
item = self.game.store_menu.validate()
self.assertIn(item, merchant.inventory)
self.assertEqual(item, merchant.inventory[1])
self.game.player.health = self.game.player.maxhealth - 1 - item.healing
self.game.handle_key_pressed(KeyValues.ENTER)
self.assertNotIn(item, self.game.player.inventory)
self.assertNotIn(item, merchant.inventory)
self.assertEqual(self.game.player.health,
self.game.player.maxhealth - 1)
# We don't have enough of money
self.game.player.hazel = 0
item = self.game.store_menu.validate()
self.game.handle_key_pressed(KeyValues.ENTER)
self.assertNotIn(item, self.game.player.inventory)
self.assertIn(item, merchant.inventory)
self.assertEqual(self.game.message, _("You do not have enough money"))
self.game.handle_key_pressed(KeyValues.ENTER)
# Exit the menu
self.game.handle_key_pressed(KeyValues.SPACE)
self.assertEqual(self.game.state, GameMode.PLAY)

View File

@ -50,6 +50,8 @@ class TestTranslations(unittest.TestCase):
"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(_("Key used to talk to a friendly entity"),
"Touche pour parler à une entité pacifique")
self.assertEqual(_("Texture pack"), "Pack de textures")
self.assertEqual(_("Language"), "Langue")
@ -64,3 +66,4 @@ class TestTranslations(unittest.TestCase):
self.assertEqual(_("body snatch potion"), "potion d'arrachage de corps")
self.assertEqual(_("bomb"), "bombe")
self.assertEqual(_("heart"), "cœur")
self.assertEqual(_("sword"), "épée")