From 646e0063bea284109aeff6703e071118fa486eba Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 13 Dec 2020 21:29:25 +0100 Subject: [PATCH 01/22] Fixed grammar, unified the docstring's format and added documentation to some classes that did not have any. Closes #32. --- squirrelbattle/display/display.py | 38 ++++++-- squirrelbattle/display/display_manager.py | 22 ++++- squirrelbattle/display/logsdisplay.py | 4 +- squirrelbattle/display/mapdisplay.py | 4 +- squirrelbattle/display/menudisplay.py | 12 ++- squirrelbattle/display/messagedisplay.py | 2 +- squirrelbattle/display/statsdisplay.py | 3 + squirrelbattle/display/texturepack.py | 3 + squirrelbattle/entities/friendly.py | 9 +- squirrelbattle/entities/items.py | 30 ++++--- squirrelbattle/entities/monsters.py | 30 +++---- squirrelbattle/entities/player.py | 13 ++- squirrelbattle/enums.py | 11 +-- squirrelbattle/game.py | 25 +++--- squirrelbattle/interfaces.py | 100 ++++++++++++---------- squirrelbattle/menus.py | 34 ++++++-- squirrelbattle/settings.py | 14 +-- squirrelbattle/term_manager.py | 2 +- squirrelbattle/tests/entities_test.py | 16 ++-- squirrelbattle/tests/game_test.py | 28 +++--- squirrelbattle/tests/interfaces_test.py | 6 +- squirrelbattle/tests/settings_test.py | 2 +- squirrelbattle/tests/translations_test.py | 4 +- squirrelbattle/translations.py | 12 +-- 24 files changed, 254 insertions(+), 170 deletions(-) diff --git a/squirrelbattle/display/display.py b/squirrelbattle/display/display.py index 29295de..f343230 100644 --- a/squirrelbattle/display/display.py +++ b/squirrelbattle/display/display.py @@ -24,9 +24,16 @@ class Display: self.pack = pack or TexturePack.get_pack("ascii") def newpad(self, height: int, width: int) -> Union[FakePad, Any]: + """ + Overwrites the native curses function of the same name. + """ return curses.newpad(height, width) if self.screen else FakePad() def truncate(self, msg: str, height: int, width: int) -> str: + """ + Truncates a string into a string adapted to the width and height of + the screen. + """ height = max(0, height) width = max(0, width) lines = msg.split("\n") @@ -36,8 +43,8 @@ class Display: def translate_color(self, color: Union[int, Tuple[int, int, int]]) -> int: """ - Translate a tuple (R, G, B) into a curses color index. - If we have already a color index, then nothing is processed. + Translates a tuple (R, G, B) into a curses color index. + If we already have a color index, then nothing is processed. If this is a tuple, we construct a new color index if non-existing and we return this index. The values of R, G and B must be between 0 and 1000, and not @@ -66,9 +73,9 @@ class Display: low: bool = False, right: bool = False, top: bool = False, vertical: bool = False, chartext: bool = False) -> None: """ - Display a message onto the pad. + Displays a message onto the pad. If the message is too large, it is truncated vertically and horizontally - The text can be bold, italic, blinking, ... if the good parameters are + The text can be bold, italic, blinking, ... if the right parameters are given. These parameters are translated into curses attributes. The foreground and background colors can be given as curses constants (curses.COLOR_*), or by giving a tuple (R, G, B) that corresponds to @@ -126,6 +133,9 @@ class Display: def resize(self, y: int, x: int, height: int, width: int, resize_pad: bool = True) -> None: + """ + Resizes a pad. + """ self.x = x self.y = y self.width = width @@ -136,6 +146,9 @@ class Display: self.pad.resize(self.height + 1, self.width + 1) def refresh(self, *args, resize_pad: bool = True) -> None: + """ + Refreshes a pad + """ if len(args) == 4: self.resize(*args, resize_pad) self.display() @@ -144,10 +157,10 @@ class Display: window_y: int, window_x: int, last_y: int, last_x: int) -> None: """ - Refresh a pad on a part of the window. + Refreshes a pad on a part of the window. The refresh starts at coordinates (top_y, top_x) from the pad, and is drawn from (window_y, window_x) to (last_y, last_x). - If coordinates are invalid (negative indexes/length..., then nothing + If coordinates are invalid (negative indexes/length...), then nothing is drawn and no error is raised. """ top_y, top_x = max(0, top_y), max(0, top_x) @@ -167,7 +180,7 @@ class Display: def handle_click(self, y: int, x: int, game: Game) -> None: """ A mouse click was performed on the coordinates (y, x) of the pad. - Maybe it can do something. + Maybe it should do something. """ pass @@ -181,7 +194,9 @@ class Display: class VerticalSplit(Display): - + """ + A class to split the screen in two vertically with a pretty line. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.pad = self.newpad(self.rows, 1) @@ -202,7 +217,9 @@ class VerticalSplit(Display): class HorizontalSplit(Display): - + """ + A class to split the screen in two horizontally with a pretty line. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.pad = self.newpad(1, self.cols) @@ -223,6 +240,9 @@ class HorizontalSplit(Display): class Box(Display): + """ + A class for pretty boxes to print menus and other content. + """ title: str = "" def update_title(self, title: str) -> None: diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index f9b3f01..b9d819c 100644 --- a/squirrelbattle/display/display_manager.py +++ b/squirrelbattle/display/display_manager.py @@ -41,6 +41,9 @@ class DisplayManager: self.update_game_components() def handle_display_action(self, action: DisplayActions, *params) -> None: + """ + Handles the differents values of display action. + """ if action == DisplayActions.REFRESH: self.refresh() elif action == DisplayActions.UPDATE: @@ -49,6 +52,9 @@ class DisplayManager: self.handle_mouse_click(*params) def update_game_components(self) -> None: + """ + Updates the game components, for example when loading a game. + """ for d in self.displays: d.pack = TexturePack.get_pack(self.game.settings.TEXTURE_PACK) self.mapdisplay.update_map(self.game.map) @@ -62,6 +68,9 @@ class DisplayManager: self.messagedisplay.update_message(self.game.message) def handle_mouse_click(self, y: int, x: int) -> None: + """ + Handles the mouse clicks. + """ displays = self.refresh() display = None for d in displays: @@ -74,6 +83,9 @@ class DisplayManager: display.handle_click(y - display.y, x - display.x, self.game) def refresh(self) -> List[Display]: + """ + Refreshes all components on the screen. + """ displays = [] if self.game.state == GameMode.PLAY \ @@ -127,7 +139,7 @@ class DisplayManager: def resize_window(self) -> bool: """ - If the window got resized, ensure that the screen size got updated. + When the window is resized, ensures that the screen size is updated. """ y, x = self.screen.getmaxyx() if self.screen else (0, 0) if self.screen and curses.is_term_resized(self.rows, @@ -138,8 +150,16 @@ class DisplayManager: @property def rows(self) -> int: + """ + Overwrites the native curses attribute of the same name, + for testing purposes. + """ return curses.LINES if self.screen else 42 @property def cols(self) -> int: + """ + Overwrites the native curses attribute of the same name, + for testing purposes. + """ return curses.COLS if self.screen else 42 diff --git a/squirrelbattle/display/logsdisplay.py b/squirrelbattle/display/logsdisplay.py index b768a0e..0aac488 100644 --- a/squirrelbattle/display/logsdisplay.py +++ b/squirrelbattle/display/logsdisplay.py @@ -6,7 +6,9 @@ from squirrelbattle.interfaces import Logs class LogsDisplay(Display): - + """ + A class to handle the display of the logs. + """ def __init__(self, *args) -> None: super().__init__(*args) self.pad = self.newpad(self.rows, self.cols) diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py index 54d9432..2b04963 100644 --- a/squirrelbattle/display/mapdisplay.py +++ b/squirrelbattle/display/mapdisplay.py @@ -6,7 +6,9 @@ from .display import Display class MapDisplay(Display): - + """ + A class to handle the display of the map. + """ def __init__(self, *args): super().__init__(*args) diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index a00d0fe..06bae1d 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -15,7 +15,7 @@ from ..translations import gettext as _ class MenuDisplay(Display): """ - A class to display the menu objects + A class to display the menu objects. """ position: int @@ -78,7 +78,7 @@ class MenuDisplay(Display): class SettingsMenuDisplay(MenuDisplay): """ - A class to display specifically a settingsmenu object + A class to display specifically a settingsmenu object. """ @property def values(self) -> List[str]: @@ -91,7 +91,7 @@ class SettingsMenuDisplay(MenuDisplay): class MainMenuDisplay(Display): """ - A class to display specifically a mainmenu object + A class to display specifically a mainmenu object. """ def __init__(self, menu: MainMenu, *args): super().__init__(*args) @@ -135,6 +135,9 @@ class MainMenuDisplay(Display): class PlayerInventoryDisplay(MenuDisplay): + """ + A class to handle the display of the player's inventory. + """ def update_pad(self) -> None: self.menubox.update_title(_("INVENTORY")) for i, item in enumerate(self.menu.values): @@ -160,6 +163,9 @@ class PlayerInventoryDisplay(MenuDisplay): class StoreInventoryDisplay(MenuDisplay): + """ + A class to handle the display of a merchant's inventory. + """ def update_pad(self) -> None: self.menubox.update_title(_("STALL")) for i, item in enumerate(self.menu.values): diff --git a/squirrelbattle/display/messagedisplay.py b/squirrelbattle/display/messagedisplay.py index 32f7139..74a98a9 100644 --- a/squirrelbattle/display/messagedisplay.py +++ b/squirrelbattle/display/messagedisplay.py @@ -7,7 +7,7 @@ from squirrelbattle.display.display import Box, Display class MessageDisplay(Display): """ - Display a message in a popup. + A class to handle the display of popup messages. """ def __init__(self, *args, **kwargs): diff --git a/squirrelbattle/display/statsdisplay.py b/squirrelbattle/display/statsdisplay.py index 9937c3e..a2fd5f4 100644 --- a/squirrelbattle/display/statsdisplay.py +++ b/squirrelbattle/display/statsdisplay.py @@ -9,6 +9,9 @@ from .display import Display class StatsDisplay(Display): + """ + A class to handle the display of the stats of the player. + """ player: Player def __init__(self, *args, **kwargs): diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index f72cd97..e4c181e 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -6,6 +6,9 @@ from typing import Any class TexturePack: + """ + A class to handle displaying several textures. + """ _packs = dict() name: str diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 6c99090..d06f35b 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -7,7 +7,7 @@ from random import choice class Merchant(InventoryHolder, FriendlyEntity): """ - The class for merchants in the dungeon + The class of merchants in the dungeon. """ def keys(self) -> list: """ @@ -28,13 +28,13 @@ class Merchant(InventoryHolder, FriendlyEntity): 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 + and allows 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. + Changes the number of hazel the merchant has by hz. """ self.hazel += hz @@ -49,4 +49,7 @@ class Sunflower(FriendlyEntity): @property def dialogue_option(self) -> list: + """ + Lists all that a sunflower can say to the player. + """ return [_("Flower power!!"), _("The sun is warm today")] diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 865a703..0661d5d 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -11,7 +11,7 @@ from ..translations import gettext as _ class Item(Entity): """ - A class for items + A class for items. """ held: bool held_by: Optional[InventoryHolder] @@ -27,7 +27,7 @@ class Item(Entity): 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: self.held_by.inventory.remove(self) @@ -48,7 +48,7 @@ class Item(Entity): def hold(self, player: InventoryHolder) -> None: """ - The item is taken from the floor and put into the inventory + The item is taken from the floor and put into the inventory. """ self.held = True self.held_by = player @@ -57,7 +57,7 @@ class Item(Entity): def save_state(self) -> dict: """ - Saves the state of the entity into a dictionary + Saves the state of the item into a dictionary. """ d = super().save_state() d["held"] = self.held @@ -65,13 +65,16 @@ class Item(Entity): @staticmethod def get_all_items() -> list: + """ + Returns the list of all item classes. + """ 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 + inventory. """ if buyer.hazel >= self.price: self.hold(buyer) @@ -85,7 +88,7 @@ class Item(Entity): class Heart(Item): """ - A heart item to return health to the player + A heart item to return health to the player. """ healing: int @@ -96,14 +99,15 @@ class Heart(Item): def hold(self, entity: InventoryHolder) -> None: """ - When holding a heart, heal the player and don't put item in inventory. + When holding a heart, the player is healed and + the item is not put in the inventory. """ entity.health = min(entity.maxhealth, entity.health + self.healing) entity.map.remove_entity(self) def save_state(self) -> dict: """ - Saves the state of the header into a dictionary + Saves the state of the heart into a dictionary. """ d = super().save_state() d["healing"] = self.healing @@ -129,7 +133,7 @@ class Bomb(Item): def use(self) -> None: """ - When the bomb is used, throw it and explodes it. + When the bomb is used, it is thrown and then it explodes. """ if self.held: self.owner = self.held_by @@ -138,7 +142,7 @@ class Bomb(Item): def act(self, m: Map) -> None: """ - Special exploding action of the bomb + Special exploding action of the bomb. """ if self.exploding: if self.tick > 0: @@ -164,7 +168,7 @@ class Bomb(Item): def save_state(self) -> dict: """ - Saves the state of the bomb into a dictionary + Saves the state of the bomb into a dictionary. """ d = super().save_state() d["exploding"] = self.exploding @@ -181,13 +185,13 @@ class Explosion(Item): def act(self, m: Map) -> None: """ - The explosion instant dies. + The bomb disappears after exploding. """ m.remove_entity(self) def hold(self, player: InventoryHolder) -> None: """ - The player can't hold any explosion. + The player can't hold an explosion. """ pass diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index 34cd4bf..5453235 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -10,8 +10,8 @@ from ..interfaces import FightingEntity, Map class Monster(FightingEntity): """ The class for all monsters in the dungeon. - A monster must override this class, and the parameters are given - in the __init__ function. + All specific monster classes overwrite this class, + and the parameters are given in the __init__ function. An example of the specification of a monster that has a strength of 4 and 20 max HP: @@ -21,7 +21,7 @@ class Monster(FightingEntity): super().__init__(name="my_monster", strength=strength, maxhealth=maxhealth, *args, **kwargs) - With that way, attributes can be overwritten when the entity got created. + With that way, attributes can be overwritten when the entity is created. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -29,7 +29,7 @@ class Monster(FightingEntity): def act(self, m: Map) -> None: """ By default, a monster will move randomly where it is possible - And if a player is close to the monster, the monster run on the player. + If the player is closeby, the monster runs to the player. """ target = None for entity in m.entities: @@ -38,12 +38,12 @@ class Monster(FightingEntity): target = entity break - # A Dijkstra algorithm has ran that targets the player. - # With that way, monsters can simply follow the path. - # If they can't move and they are already close to the player, - # They hit. + # Monsters move according to a Dijkstra algorithm + # that targets the player. + # If they can not move and are already close to the player, + # they hit. if target and (self.y, self.x) in target.paths: - # Move to target player by choosing the best avaliable path + # Moves to target player by choosing the best available path for next_y, next_x in target.paths[(self.y, self.x)]: moved = self.check_move(next_y, next_x, True) if moved: @@ -52,8 +52,8 @@ class Monster(FightingEntity): self.map.logs.add_message(self.hit(target)) break else: - # Move in a random direction - # If the direction is not available, try another one + # Moves in a random direction + # If the direction is not available, tries another one moves = [self.move_up, self.move_down, self.move_left, self.move_right] shuffle(moves) @@ -64,7 +64,7 @@ class Monster(FightingEntity): class Tiger(Monster): """ - A tiger monster + A tiger monster. """ def __init__(self, name: str = "tiger", strength: int = 2, maxhealth: int = 20, *args, **kwargs) -> None: @@ -74,7 +74,7 @@ class Tiger(Monster): class Hedgehog(Monster): """ - A really mean hedgehog monster + A really mean hedgehog monster. """ def __init__(self, name: str = "hedgehog", strength: int = 3, maxhealth: int = 10, *args, **kwargs) -> None: @@ -84,7 +84,7 @@ class Hedgehog(Monster): class Rabbit(Monster): """ - A rabbit monster + A rabbit monster. """ def __init__(self, name: str = "rabbit", strength: int = 1, maxhealth: int = 15, *args, **kwargs) -> None: @@ -94,7 +94,7 @@ class Rabbit(Monster): class TeddyBear(Monster): """ - A cute teddybear monster + A cute teddybear monster. """ def __init__(self, name: str = "teddy_bear", strength: int = 0, maxhealth: int = 50, *args, **kwargs) -> None: diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 19c8348..36b497f 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -11,7 +11,7 @@ from ..interfaces import FightingEntity, InventoryHolder class Player(InventoryHolder, FightingEntity): """ - The class of the player + The class of the player. """ current_xp: int = 0 max_xp: int = 10 @@ -45,7 +45,7 @@ class Player(InventoryHolder, FightingEntity): def level_up(self) -> None: """ - Add levels to the player as much as it is possible. + Add as many levels as possible to the player. """ while self.current_xp > self.max_xp: self.level += 1 @@ -59,8 +59,8 @@ class Player(InventoryHolder, FightingEntity): def add_xp(self, xp: int) -> None: """ - Add some experience to the player. - If the required amount is reached, level up. + Adds some experience to the player. + If the required amount is reached, the player levels up. """ self.current_xp += xp self.level_up() @@ -89,9 +89,8 @@ class Player(InventoryHolder, FightingEntity): def recalculate_paths(self, max_distance: int = 8) -> None: """ - Use Dijkstra algorithm to calculate best paths for monsters to go to - the player. Actually, the paths are computed for each tile adjacent to - the player then for each step the monsters use the best path avaliable. + Uses Dijkstra algorithm to calculate best paths for monsters to go to + the player. """ distances = [] predecessors = [] diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index 7e4efa4..d248d29 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -16,12 +16,11 @@ class DisplayActions(Enum): """ REFRESH = auto() UPDATE = auto() - MOUSE = auto() class GameMode(Enum): """ - Game mode options + Game mode options. """ MAINMENU = auto() PLAY = auto() @@ -32,9 +31,8 @@ class GameMode(Enum): class KeyValues(Enum): """ - Key values options used in the game + Key values options used in the game. """ - MOUSE = auto() UP = auto() DOWN = auto() LEFT = auto() @@ -46,12 +44,11 @@ class KeyValues(Enum): DROP = auto() SPACE = auto() CHAT = auto() - WAIT = auto() @staticmethod def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]: """ - Translate the raw string key into an enum value that we can use. + Translates the raw string key into an enum value that we can use. """ if key in (settings.KEY_DOWN_SECONDARY, settings.KEY_DOWN_PRIMARY): @@ -79,6 +76,4 @@ class KeyValues(Enum): return KeyValues.SPACE elif key == settings.KEY_CHAT: return KeyValues.CHAT - elif key == settings.KEY_WAIT: - return KeyValues.WAIT return None diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index ed3b60f..35c0b0f 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -30,7 +30,7 @@ class Game: def __init__(self) -> None: """ - Init the game. + Initiates the game. """ self.state = GameMode.MAINMENU self.waiting_for_friendly_key = False @@ -48,7 +48,7 @@ class Game: def new_game(self) -> None: """ - Create a new game on the screen. + Creates a new game on the screen. """ # TODO generate a new map procedurally self.map = Map.load(ResourceManager.get_asset_path("example_map.txt")) @@ -63,8 +63,8 @@ class Game: def run(self, screen: Any) -> None: """ Main infinite loop. - We wait for the player's action, then we do what that should be done - when the given key gets pressed. + We wait for the player's action, then we do what should be done + when a key gets pressed. """ while True: # pragma no cover screen.erase() @@ -81,7 +81,7 @@ class Game: def handle_key_pressed(self, key: Optional[KeyValues], raw_key: str = '')\ -> None: """ - Indicates what should be done when the given key is pressed, + Indicates what should be done when a given key is pressed, according to the current game state. """ if self.message: @@ -133,8 +133,9 @@ class Game: 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 the player tries to talk to a friendly entity, the game waits for + a directional key to be pressed, verifies there is a friendly entity + in that direction and then lets the player interact with it. """ if not self.waiting_for_friendly_key: return @@ -210,7 +211,7 @@ class Game: 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 different options. """ if key == KeyValues.DOWN: self.main_menu.go_down() @@ -235,13 +236,13 @@ class Game: def save_state(self) -> dict: """ - Saves the game to a dictionary + Saves the game to a dictionary. """ return self.map.save_state() def load_state(self, d: dict) -> None: """ - Loads the game from a dictionary + Loads the game from a dictionary. """ try: self.map.load_state(d) @@ -265,7 +266,7 @@ class Game: def load_game(self) -> None: """ - Loads the game from a file + Loads the game from a file. """ file_path = ResourceManager.get_config_path("save.json") if os.path.isfile(file_path): @@ -282,7 +283,7 @@ class Game: def save_game(self) -> None: """ - Saves the game to a file + Saves the game to a file. """ with open(ResourceManager.get_config_path("save.json"), "w") as f: f.write(json.dumps(self.save_state())) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 94025bd..71f70ad 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -12,7 +12,7 @@ from .translations import gettext as _ class Logs: """ - The logs object stores the messages to display. It is encapsulating a list + The logs object stores the messages to display. It encapsulates a list of such messages, to allow multiple pointers to keep track of it even if the list was to be reassigned. """ @@ -32,7 +32,7 @@ class Logs: class Map: """ - Object that represents a Map with its width, height + The Map object represents a with its width, height and tiles, that have their custom properties. """ width: int @@ -59,14 +59,14 @@ class Map: def add_entity(self, entity: "Entity") -> None: """ - Register a new entity in the map. + Registers a new entity in the map. """ self.entities.append(entity) entity.map = self def remove_entity(self, entity: "Entity") -> None: """ - Unregister an entity from the map. + Unregisters an entity from the map. """ if entity in self.entities: self.entities.remove(entity) @@ -86,7 +86,7 @@ class Map: def entity_is_present(self, y: int, x: int) -> bool: """ Indicates that the tile at the coordinates (y, x) contains a killable - entity + entity. """ return 0 <= y < self.height and 0 <= x < self.width and \ any(entity.x == x and entity.y == y and entity.is_friendly() @@ -95,7 +95,7 @@ class Map: @staticmethod def load(filename: str) -> "Map": """ - Read a file that contains the content of a map, and build a Map object. + Reads a file that contains the content of a map, and builds a Map object. """ with open(filename, "r") as f: file = f.read() @@ -104,7 +104,7 @@ class Map: @staticmethod def load_from_string(content: str) -> "Map": """ - Load a map represented by its characters and build a Map object. + Loads a map represented by its characters and builds a Map object. """ lines = content.split("\n") first_line = lines[0] @@ -120,7 +120,7 @@ class Map: @staticmethod def load_dungeon_from_string(content: str) -> List[List["Tile"]]: """ - Transforms a string into the list of corresponding tiles + Transforms a string into the list of corresponding tiles. """ lines = content.split("\n") tiles = [[Tile.from_ascii_char(c) @@ -129,7 +129,7 @@ class Map: def draw_string(self, pack: TexturePack) -> str: """ - Draw the current map as a string object that can be rendered + Draws the current map as a string object that can be rendered in the window. """ return "\n".join("".join(tile.char(pack) for tile in line) @@ -137,7 +137,7 @@ class Map: def spawn_random_entities(self, count: int) -> None: """ - Put randomly {count} entities on the map, where it is available. + Puts randomly {count} entities on the map, only on empty ground tiles. """ for ignored in range(count): y, x = 0, 0 @@ -152,14 +152,14 @@ class Map: def tick(self) -> None: """ - Trigger all entity events. + Triggers all entity events. """ for entity in self.entities: entity.act(self) def save_state(self) -> dict: """ - Saves the map's attributes to a dictionary + Saves the map's attributes to a dictionary. """ d = dict() d["width"] = self.width @@ -176,7 +176,7 @@ class Map: def load_state(self, d: dict) -> None: """ - Loads the map's attributes from a dictionary + Loads the map's attributes from a dictionary. """ self.width = d["width"] self.height = d["height"] @@ -193,7 +193,7 @@ class Map: class Tile(Enum): """ - The internal representation of the tiles of the map + The internal representation of the tiles of the map. """ EMPTY = auto() WALL = auto() @@ -202,7 +202,7 @@ class Tile(Enum): @staticmethod def from_ascii_char(ch: str) -> "Tile": """ - Maps an ascii character to its equivalent in the texture pack + Maps an ascii character to its equivalent in the texture pack. """ for tile in Tile: if tile.char(TexturePack.ASCII_PACK) == ch: @@ -212,7 +212,7 @@ class Tile(Enum): def char(self, pack: TexturePack) -> str: """ Translates a Tile to the corresponding character according - to the texture pack + to the texture pack. """ return getattr(pack, self.name) @@ -224,14 +224,14 @@ class Tile(Enum): def can_walk(self) -> bool: """ - Check if an entity (player or not) can move in this tile. + Checks if an entity (player or not) can move in this tile. """ return not self.is_wall() and self != Tile.EMPTY class Entity: """ - An Entity object represents any entity present on the map + An Entity object represents any entity present on the map. """ y: int x: int @@ -249,7 +249,7 @@ class Entity: def check_move(self, y: int, x: int, move_if_possible: bool = False)\ -> bool: """ - Checks if moving to (y,x) is authorized + Checks if moving to (y,x) is authorized. """ free = self.map.is_free(y, x) if free and move_if_possible: @@ -258,7 +258,7 @@ class Entity: def move(self, y: int, x: int) -> bool: """ - Moves an entity to (y,x) coordinates + Moves an entity to (y,x) coordinates. """ self.y = y self.x = x @@ -266,49 +266,49 @@ class Entity: def move_up(self, force: bool = False) -> bool: """ - Moves the entity up one tile, if possible + Moves the entity up one tile, if possible. """ return self.move(self.y - 1, self.x) if force else \ self.check_move(self.y - 1, self.x, True) def move_down(self, force: bool = False) -> bool: """ - Moves the entity down one tile, if possible + Moves the entity down one tile, if possible. """ return self.move(self.y + 1, self.x) if force else \ self.check_move(self.y + 1, self.x, True) def move_left(self, force: bool = False) -> bool: """ - Moves the entity left one tile, if possible + Moves the entity left one tile, if possible. """ return self.move(self.y, self.x - 1) if force else \ self.check_move(self.y, self.x - 1, True) def move_right(self, force: bool = False) -> bool: """ - Moves the entity right one tile, if possible + Moves the entity right one tile, if possible. """ return self.move(self.y, self.x + 1) if force else \ self.check_move(self.y, self.x + 1, True) def act(self, m: Map) -> None: """ - Define the action of the entity that is ran each tick. + Defines the action the entity will do at each tick. By default, does nothing. """ pass def distance_squared(self, other: "Entity") -> int: """ - Get the square of the distance to another entity. - Useful to check distances since square root takes time. + Gives the square of the distance to another entity. + Useful to check distances since taking the square root takes time. """ return (self.y - other.y) ** 2 + (self.x - other.x) ** 2 def distance(self, other: "Entity") -> float: """ - Get the cartesian distance to another entity. + Gives the cartesian distance to another entity. """ return sqrt(self.distance_squared(other)) @@ -340,12 +340,15 @@ class Entity: @property def translated_name(self) -> str: + """ + Translates the name of entities. + """ return _(self.name.replace("_", " ")) @staticmethod def get_all_entity_classes() -> list: """ - Returns all entities subclasses + Returns all entities subclasses. """ from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart from squirrelbattle.entities.monsters import Tiger, Hedgehog, \ @@ -357,7 +360,7 @@ class Entity: @staticmethod def get_all_entity_classes_in_a_dict() -> dict: """ - Returns all entities subclasses in a dictionary + Returns all entities subclasses in a dictionary. """ from squirrelbattle.entities.player import Player from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \ @@ -381,7 +384,7 @@ class Entity: def save_state(self) -> dict: """ - Saves the coordinates of the entity + Saves the coordinates of the entity. """ d = dict() d["x"] = self.x @@ -393,7 +396,7 @@ class Entity: class FightingEntity(Entity): """ A FightingEntity is an entity that can fight, and thus has a health, - level and stats + level and stats. """ maxhealth: int health: int @@ -420,11 +423,15 @@ class FightingEntity(Entity): @property def dead(self) -> bool: + """ + Is this entity dead ? + """ return self.health <= 0 def hit(self, opponent: "FightingEntity") -> str: """ - Deals damage to the opponent, based on the stats + The entity deals damage to the opponent + based on their respective stats. """ return _("{name} hits {opponent}.")\ .format(name=_(self.translated_name.capitalize()), @@ -433,7 +440,8 @@ class FightingEntity(Entity): def take_damage(self, attacker: "Entity", amount: int) -> str: """ - Take damage from the attacker, based on the stats + The entity takes damage from the attacker + based on their respective stats. """ self.health -= amount if self.health <= 0: @@ -446,20 +454,20 @@ class FightingEntity(Entity): def die(self) -> None: """ - If a fighting entity has no more health, it dies and is removed + If a fighting entity has no more health, it dies and is removed. """ self.map.remove_entity(self) def keys(self) -> list: """ - Returns a fighting entity's specific attributes + Returns a fighting entity's specific attributes. """ return ["name", "maxhealth", "health", "level", "strength", "intelligence", "charisma", "dexterity", "constitution"] def save_state(self) -> dict: """ - Saves the state of the entity into a dictionary + Saves the state of the entity into a dictionary. """ d = super().save_state() for name in self.keys(): @@ -469,7 +477,7 @@ class FightingEntity(Entity): class FriendlyEntity(FightingEntity): """ - Friendly entities are living entities which do not attack the player + Friendly entities are living entities which do not attack the player. """ dialogue_option: list @@ -480,7 +488,7 @@ class FriendlyEntity(FightingEntity): def keys(self) -> list: """ - Returns a friendly entity's specific attributes + Returns a friendly entity's specific attributes. """ return ["maxhealth", "health"] @@ -491,7 +499,7 @@ class InventoryHolder(Entity): def translate_inventory(self, inventory: list) -> list: """ - Translate the JSON-state of the inventory into a list of the items in + Translates the JSON save of the inventory into a list of the items in the inventory. """ for i in range(len(inventory)): @@ -501,7 +509,7 @@ class InventoryHolder(Entity): def dict_to_inventory(self, item_dict: dict) -> Entity: """ - Translate a dict object that contains the state of an item + Translates a dictionnary that contains the state of an item into an item object. """ entity_classes = self.get_all_entity_classes_in_a_dict() @@ -511,7 +519,7 @@ class InventoryHolder(Entity): def save_state(self) -> dict: """ - We save the inventory of the merchant formatted as JSON + The inventory of the merchant is saved in a JSON format. """ d = super().save_state() d["hazel"] = self.hazel @@ -520,19 +528,19 @@ class InventoryHolder(Entity): def add_to_inventory(self, obj: Any) -> None: """ - Adds an object to inventory + Adds an object to the inventory. """ self.inventory.append(obj) def remove_from_inventory(self, obj: Any) -> None: """ - Removes an object from the inventory + 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 + Changes the number of hazel the entity has by hz. hz is negative + when the entity loses money and positive when it gains money. """ self.hazel += hz diff --git a/squirrelbattle/menus.py b/squirrelbattle/menus.py index e0087a3..e6e8cef 100644 --- a/squirrelbattle/menus.py +++ b/squirrelbattle/menus.py @@ -14,7 +14,7 @@ from .translations import gettext as _, Translator class Menu: """ - A Menu object is the logical representation of a menu in the game + A Menu object is the logical representation of a menu in the game. """ values: list @@ -23,26 +23,26 @@ class Menu: def go_up(self) -> None: """ - Moves the pointer of the menu on the previous value + Moves the pointer of the menu on the previous value. """ self.position = max(0, self.position - 1) def go_down(self) -> None: """ - Moves the pointer of the menu on the next value + Moves the pointer of the menu on the next value. """ self.position = min(len(self.values) - 1, self.position + 1) def validate(self) -> Any: """ - Selects the value that is pointed by the menu pointer + Selects the value that is pointed by the menu pointer. """ return self.values[self.position] class MainMenuValues(Enum): """ - Values of the main menu + Values of the main menu. """ START = "New game" RESUME = "Resume" @@ -57,14 +57,14 @@ class MainMenuValues(Enum): class MainMenu(Menu): """ - A special instance of a menu : the main menu + A special instance of a menu : the main menu. """ values = [e for e in MainMenuValues] class SettingsMenu(Menu): """ - A special instance of a menu : the settings menu + A special instance of a menu : the settings menu. """ waiting_for_key: bool = False @@ -75,7 +75,7 @@ class SettingsMenu(Menu): def handle_key_pressed(self, key: Optional[KeyValues], raw_key: str, game: Any) -> None: """ - In the setting menu, we van select a setting and change it + In the setting menu, we can select a setting and change it. """ if not self.waiting_for_key: # Navigate normally through the menu. @@ -121,22 +121,40 @@ class SettingsMenu(Menu): class InventoryMenu(Menu): + """ + A special instance of a menu : the menu for the inventory of the player. + """ player: Player def update_player(self, player: Player) -> None: + """ + Updates the player. + """ self.player = player @property def values(self) -> list: + """ + Returns the values of the menu. + """ return self.player.inventory class StoreMenu(Menu): + """ + A special instance of a menu : the menu for the inventory of a merchant. + """ merchant: Merchant def update_merchant(self, merchant: Merchant) -> None: + """ + Updates the merchant. + """ self.merchant = merchant @property def values(self) -> list: + """ + Returns the values of the menu. + """ return self.merchant.inventory diff --git a/squirrelbattle/settings.py b/squirrelbattle/settings.py index 91edfa4..3ff1be7 100644 --- a/squirrelbattle/settings.py +++ b/squirrelbattle/settings.py @@ -13,9 +13,9 @@ from .translations import gettext as _ class Settings: """ This class stores the settings of the game. - Settings can be get by using for example settings.TEXTURE_PACK directly. - The comment can be get by using settings.get_comment('TEXTURE_PACK'). - We can define the setting by simply use settings.TEXTURE_PACK = 'new_key' + Settings can be obtained by using for example settings.TEXTURE_PACK directly. + The comment can be obtained by using settings.get_comment('TEXTURE_PACK'). + We can set the setting by simply using settings.TEXTURE_PACK = 'new_key' """ def __init__(self): self.KEY_UP_PRIMARY = ['z', 'Main key to move up'] @@ -50,7 +50,7 @@ class Settings: def get_comment(self, item: str) -> str: """ - Retrieve the comment of a setting. + Retrieves the comment relative to a setting. """ if item in self.settings_keys: return _(object.__getattribute__(self, item)[1]) @@ -61,13 +61,13 @@ class Settings: @property def settings_keys(self) -> Generator[str, Any, None]: """ - Get the list of all parameters. + Gets the list of all parameters. """ return (key for key in self.__dict__) def loads_from_string(self, json_str: str) -> None: """ - Dump settings + Loads settings. """ d = json.loads(json_str) for key in d: @@ -75,7 +75,7 @@ class Settings: def dumps_to_string(self) -> str: """ - Dump settings + Dumps settings. """ d = dict() for key in self.settings_keys: diff --git a/squirrelbattle/term_manager.py b/squirrelbattle/term_manager.py index 5a98a4a..6484289 100644 --- a/squirrelbattle/term_manager.py +++ b/squirrelbattle/term_manager.py @@ -8,7 +8,7 @@ from types import TracebackType class TermManager: # pragma: no cover """ The TermManager object initializes the terminal, returns a screen object and - de-initializes the terminal after use + de-initializes the terminal after use. """ def __init__(self): self.screen = curses.initscr() diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 70e3748..f1ffb16 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -14,7 +14,7 @@ from squirrelbattle.resources import ResourceManager class TestEntities(unittest.TestCase): def setUp(self) -> None: """ - Load example map that can be used in tests. + Loads example map that can be used in tests. """ self.map = Map.load(ResourceManager.get_asset_path("example_map.txt")) self.player = Player() @@ -23,7 +23,7 @@ class TestEntities(unittest.TestCase): def test_basic_entities(self) -> None: """ - Test some random stuff with basic entities. + Tests some random stuff with basic entities. """ entity = Entity() entity.move(42, 64) @@ -38,7 +38,7 @@ class TestEntities(unittest.TestCase): def test_fighting_entities(self) -> None: """ - Test some random stuff with fighting entities. + Tests some random stuff with fighting entities. """ entity = Tiger() self.map.add_entity(entity) @@ -91,7 +91,7 @@ class TestEntities(unittest.TestCase): def test_items(self) -> None: """ - Test some random stuff with items. + Tests some random stuff with items. """ item = Item() self.map.add_entity(item) @@ -112,7 +112,7 @@ class TestEntities(unittest.TestCase): def test_bombs(self) -> None: """ - Test some random stuff with bombs. + Tests some random stuff with bombs. """ item = Bomb() hedgehog = Hedgehog() @@ -156,7 +156,7 @@ class TestEntities(unittest.TestCase): def test_hearts(self) -> None: """ - Test some random stuff with hearts. + Tests some random stuff with hearts. """ item = Heart() self.map.add_entity(item) @@ -171,7 +171,7 @@ class TestEntities(unittest.TestCase): def test_body_snatch_potion(self) -> None: """ - Test some random stuff with body snatch potions. + Tests some random stuff with body snatch potions. """ item = BodySnatchPotion() self.map.add_entity(item) @@ -189,7 +189,7 @@ class TestEntities(unittest.TestCase): def test_players(self) -> None: """ - Test some random stuff with players. + Tests some random stuff with players. """ player = Player() self.map.add_entity(player) diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 52aeeaf..750335f 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -21,7 +21,7 @@ from ..translations import gettext as _, Translator class TestGame(unittest.TestCase): def setUp(self) -> None: """ - Setup game. + Sets the game up. """ self.game = Game() self.game.new_game() @@ -31,7 +31,7 @@ class TestGame(unittest.TestCase): def test_load_game(self) -> None: """ - Save a game and reload it. + Saves a game and reloads it. """ bomb = Bomb() self.game.map.add_entity(bomb) @@ -85,7 +85,7 @@ class TestGame(unittest.TestCase): def test_bootstrap_fail(self) -> None: """ - Ensure that the test can't play the game, + Ensures that the test can't play the game, because there is no associated shell. Yeah, that's only for coverage. """ @@ -94,7 +94,7 @@ class TestGame(unittest.TestCase): def test_key_translation(self) -> None: """ - Test key bindings. + Tests key bindings. """ self.game.settings = Settings() @@ -150,7 +150,7 @@ class TestGame(unittest.TestCase): def test_key_press(self) -> None: """ - Press a key and see what is done. + Presses a key and asserts what is done is correct. """ self.assertEqual(self.game.state, GameMode.MAINMENU) self.assertEqual(self.game.main_menu.validate(), @@ -241,7 +241,7 @@ class TestGame(unittest.TestCase): def test_mouse_click(self) -> None: """ - Simulate mouse clicks. + Simulates mouse clicks. """ self.game.state = GameMode.MAINMENU @@ -271,7 +271,7 @@ class TestGame(unittest.TestCase): def test_new_game(self) -> None: """ - Ensure that the start button starts a new game. + Ensures that the start button starts a new game. """ old_map = self.game.map old_player = self.game.player @@ -294,7 +294,7 @@ class TestGame(unittest.TestCase): def test_settings_menu(self) -> None: """ - Ensure that the settings menu is working properly. + Ensures that the settings menu is working properly. """ self.game.settings = Settings() @@ -380,7 +380,7 @@ class TestGame(unittest.TestCase): def test_dead_screen(self) -> None: """ - Kill player and render dead screen. + Kills the player and renders the dead message on the fake screen. """ self.game.state = GameMode.PLAY # Kill player @@ -396,13 +396,13 @@ class TestGame(unittest.TestCase): def test_not_implemented(self) -> None: """ - Check that some functions are not implemented, only for coverage. + Checks that some functions are not implemented, only for coverage. """ self.assertRaises(NotImplementedError, Display.display, None) def test_messages(self) -> None: """ - Display error messages. + Displays error messages. """ self.game.message = "I am an error" self.game.display_actions(DisplayActions.UPDATE) @@ -412,7 +412,7 @@ class TestGame(unittest.TestCase): def test_inventory_menu(self) -> None: """ - Open the inventory menu and interact with items. + Opens the inventory menu and interacts with items. """ self.game.state = GameMode.PLAY # Open and close the inventory @@ -473,7 +473,7 @@ class TestGame(unittest.TestCase): def test_talk_to_sunflowers(self) -> None: """ - Interact with sunflowers + Interacts with sunflowers. """ self.game.state = GameMode.PLAY @@ -524,7 +524,7 @@ class TestGame(unittest.TestCase): def test_talk_to_merchant(self) -> None: """ - Interact with merchants + Interacts with merchants. """ self.game.state = GameMode.PLAY diff --git a/squirrelbattle/tests/interfaces_test.py b/squirrelbattle/tests/interfaces_test.py index c9f7253..6f2a4bb 100644 --- a/squirrelbattle/tests/interfaces_test.py +++ b/squirrelbattle/tests/interfaces_test.py @@ -11,7 +11,7 @@ from squirrelbattle.resources import ResourceManager class TestInterfaces(unittest.TestCase): def test_map(self) -> None: """ - Create a map and check that it is well parsed. + Creates a map and checks that it is well parsed. """ m = Map.load_from_string("0 0\n.#\n#.\n") self.assertEqual(m.width, 2) @@ -20,7 +20,7 @@ class TestInterfaces(unittest.TestCase): def test_load_map(self) -> None: """ - Try to load a map from a file. + Tries to load a map from a file. """ m = Map.load(ResourceManager.get_asset_path("example_map.txt")) self.assertEqual(m.width, 52) @@ -28,7 +28,7 @@ class TestInterfaces(unittest.TestCase): def test_tiles(self) -> None: """ - Test some things about tiles. + Tests some things about tiles. """ self.assertFalse(Tile.FLOOR.is_wall()) self.assertTrue(Tile.WALL.is_wall()) diff --git a/squirrelbattle/tests/settings_test.py b/squirrelbattle/tests/settings_test.py index 06225b2..65cb25a 100644 --- a/squirrelbattle/tests/settings_test.py +++ b/squirrelbattle/tests/settings_test.py @@ -13,7 +13,7 @@ class TestSettings(unittest.TestCase): def test_settings(self) -> None: """ - Ensure that settings are well loaded. + Ensures that settings are well loaded. """ settings = Settings() self.assertEqual(settings.KEY_UP_PRIMARY, 'z') diff --git a/squirrelbattle/tests/translations_test.py b/squirrelbattle/tests/translations_test.py index 0bd8873..72b8562 100644 --- a/squirrelbattle/tests/translations_test.py +++ b/squirrelbattle/tests/translations_test.py @@ -11,7 +11,7 @@ class TestTranslations(unittest.TestCase): def test_main_menu_translation(self) -> None: """ - Ensure that the main menu is translated. + Ensures that the main menu is translated. """ self.assertEqual(_("New game"), "Nouvelle partie") self.assertEqual(_("Resume"), "Continuer") @@ -22,7 +22,7 @@ class TestTranslations(unittest.TestCase): def test_settings_menu_translation(self) -> None: """ - Ensure that the settings menu is translated. + Ensures that the settings menu is translated. """ self.assertEqual(_("Main key to move up"), "Touche principale pour aller vers le haut") diff --git a/squirrelbattle/translations.py b/squirrelbattle/translations.py index 08d40d1..df140a2 100644 --- a/squirrelbattle/translations.py +++ b/squirrelbattle/translations.py @@ -13,7 +13,7 @@ class Translator: """ This module uses gettext to translate strings. Translator.setlocale defines the language of the strings, - then gettext() translates the message. + then gettext() translates the messages. """ SUPPORTED_LOCALES: List[str] = ["de", "en", "es", "fr"] locale: str = "en" @@ -22,7 +22,7 @@ class Translator: @classmethod def refresh_translations(cls) -> None: """ - Load compiled translations. + Loads compiled translations. """ for language in cls.SUPPORTED_LOCALES: rep = Path(__file__).parent / "locale" / language / "LC_MESSAGES" @@ -37,7 +37,7 @@ class Translator: @classmethod def setlocale(cls, lang: str) -> None: """ - Define the language used to translate the game. + Defines the language used to translate the game. The language must be supported, otherwise nothing is done. """ lang = lang[:2] @@ -51,7 +51,7 @@ class Translator: @classmethod def makemessages(cls) -> None: # pragma: no cover """ - Analyse all strings in the project and extract them. + Analyses all strings in the project and extracts them. """ for language in cls.SUPPORTED_LOCALES: if language == "en": @@ -83,7 +83,7 @@ class Translator: @classmethod def compilemessages(cls) -> None: """ - Compile translation messages from source files. + Compiles translation messages from source files. """ for language in cls.SUPPORTED_LOCALES: if language == "en": @@ -99,7 +99,7 @@ class Translator: def gettext(message: str) -> str: """ - Translate a message. + Translates a message. """ return Translator.get_translator().gettext(message) From a3e059a97bc743315064508538f19cf7463cad97 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 15 Dec 2020 17:37:23 +0100 Subject: [PATCH 02/22] Some required code mysteriously disappeared --- squirrelbattle/enums.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index d248d29..d9b0735 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -16,6 +16,7 @@ class DisplayActions(Enum): """ REFRESH = auto() UPDATE = auto() + MOUSE = auto() class GameMode(Enum): @@ -44,6 +45,7 @@ class KeyValues(Enum): DROP = auto() SPACE = auto() CHAT = auto() + WAIT = auto() @staticmethod def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]: @@ -76,4 +78,6 @@ class KeyValues(Enum): return KeyValues.SPACE elif key == settings.KEY_CHAT: return KeyValues.CHAT + elif key == settings.KEY_WAIT: + return KeyValues.WAIT return None From 8ecbf13eae822864d5fdb24e62d86e724ff07374 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 18 Dec 2020 15:31:23 +0100 Subject: [PATCH 03/22] Being able to calculate paths is now a property of fighting entities. --- squirrelbattle/entities/player.py | 52 ---------------------------- squirrelbattle/interfaces.py | 57 ++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 36b497f..5f389cf 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -1,8 +1,6 @@ # Copyright (C) 2020 by ΓΏnΓ©rant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later -from functools import reduce -from queue import PriorityQueue from random import randint from typing import Dict, Tuple @@ -15,7 +13,6 @@ class Player(InventoryHolder, FightingEntity): """ current_xp: int = 0 max_xp: int = 10 - 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, @@ -87,55 +84,6 @@ class Player(InventoryHolder, FightingEntity): entity.hold(self) return super().check_move(y, x, move_if_possible) - def recalculate_paths(self, max_distance: int = 8) -> None: - """ - Uses Dijkstra algorithm to calculate best paths for monsters to go to - the player. - """ - distances = [] - predecessors = [] - # four Dijkstras, one for each adjacent tile - for dir_y, dir_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]: - queue = PriorityQueue() - new_y, new_x = self.y + dir_y, self.x + dir_x - if not 0 <= new_y < self.map.height or \ - not 0 <= new_x < self.map.width or \ - not self.map.tiles[new_y][new_x].can_walk(): - continue - queue.put(((1, 0), (new_y, new_x))) - visited = [(self.y, self.x)] - distances.append({(self.y, self.x): (0, 0), (new_y, new_x): (1, 0)}) - predecessors.append({(new_y, new_x): (self.y, self.x)}) - while not queue.empty(): - dist, (y, x) = queue.get() - if dist[0] >= max_distance or (y, x) in visited: - continue - visited.append((y, x)) - for diff_y, diff_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]: - new_y, new_x = y + diff_y, x + diff_x - if not 0 <= new_y < self.map.height or \ - not 0 <= new_x < self.map.width or \ - not self.map.tiles[new_y][new_x].can_walk(): - continue - new_distance = (dist[0] + 1, - dist[1] + (not self.map.is_free(y, x))) - if not (new_y, new_x) in distances[-1] or \ - distances[-1][(new_y, new_x)] > new_distance: - predecessors[-1][(new_y, new_x)] = (y, x) - distances[-1][(new_y, new_x)] = new_distance - queue.put((new_distance, (new_y, new_x))) - # For each tile that is reached by at least one Dijkstra, sort the - # different paths by distance to the player. For the technical bits : - # The reduce function is a fold starting on the first element of the - # iterable, and we associate the points to their distance, sort - # along the distance, then only keep the points. - self.paths = {} - for y, x in reduce(set.union, - [set(p.keys()) for p in predecessors], set()): - self.paths[(y, x)] = [p for d, p in sorted( - [(distances[i][(y, x)], predecessors[i][(y, x)]) - for i in range(len(distances)) if (y, x) in predecessors[i]])] - def save_state(self) -> dict: """ Saves the state of the entity into a dictionary diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 71f70ad..afe4c44 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -4,7 +4,9 @@ from enum import Enum, auto from math import sqrt from random import choice, randint -from typing import List, Optional, Any +from typing import List, Optional, Any, Dict, Tuple +from queue import PriorityQueue +from functools import reduce from .display.texturepack import TexturePack from .translations import gettext as _ @@ -237,6 +239,7 @@ class Entity: x: int name: str map: Map + paths: Dict[Tuple[int, int], Tuple[int, int]] # noinspection PyShadowingBuiltins def __init__(self, y: int = 0, x: int = 0, name: Optional[str] = None, @@ -245,6 +248,7 @@ class Entity: self.x = x self.name = name self.map = map + self.paths = None def check_move(self, y: int, x: int, move_if_possible: bool = False)\ -> bool: @@ -292,6 +296,57 @@ class Entity: return self.move(self.y, self.x + 1) if force else \ self.check_move(self.y, self.x + 1, True) + def recalculate_paths(self, max_distance: int = 8) -> None: + """ + Uses Dijkstra algorithm to calculate best paths for other entities to + go to this entity. If self.paths is None, does nothing. + """ + if self.paths == None : + return + distances = [] + predecessors = [] + # four Dijkstras, one for each adjacent tile + for dir_y, dir_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]: + queue = PriorityQueue() + new_y, new_x = self.y + dir_y, self.x + dir_x + if not 0 <= new_y < self.map.height or \ + not 0 <= new_x < self.map.width or \ + not self.map.tiles[new_y][new_x].can_walk(): + continue + queue.put(((1, 0), (new_y, new_x))) + visited = [(self.y, self.x)] + distances.append({(self.y, self.x): (0, 0), (new_y, new_x): (1, 0)}) + predecessors.append({(new_y, new_x): (self.y, self.x)}) + while not queue.empty(): + dist, (y, x) = queue.get() + if dist[0] >= max_distance or (y, x) in visited: + continue + visited.append((y, x)) + for diff_y, diff_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]: + new_y, new_x = y + diff_y, x + diff_x + if not 0 <= new_y < self.map.height or \ + not 0 <= new_x < self.map.width or \ + not self.map.tiles[new_y][new_x].can_walk(): + continue + new_distance = (dist[0] + 1, + dist[1] + (not self.map.is_free(y, x))) + if not (new_y, new_x) in distances[-1] or \ + distances[-1][(new_y, new_x)] > new_distance: + predecessors[-1][(new_y, new_x)] = (y, x) + distances[-1][(new_y, new_x)] = new_distance + queue.put((new_distance, (new_y, new_x))) + # For each tile that is reached by at least one Dijkstra, sort the + # different paths by distance to the player. For the technical bits : + # The reduce function is a fold starting on the first element of the + # iterable, and we associate the points to their distance, sort + # along the distance, then only keep the points. + self.paths = {} + for y, x in reduce(set.union, + [set(p.keys()) for p in predecessors], set()): + self.paths[(y, x)] = [p for d, p in sorted( + [(distances[i][(y, x)], predecessors[i][(y, x)]) + for i in range(len(distances)) if (y, x) in predecessors[i]])] + def act(self, m: Map) -> None: """ Defines the action the entity will do at each tick. From dadafc84eb57670df6298ed32e1248aa9f202f68 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 18 Dec 2020 17:29:59 +0100 Subject: [PATCH 04/22] Added a familiar class that follows the player around and hits monsters when it sees one. Added a trumpet, an instance of familiar. Closes #46. --- squirrelbattle/display/texturepack.py | 3 ++ squirrelbattle/entities/friendly.py | 71 +++++++++++++++++++++++++-- squirrelbattle/entities/monsters.py | 7 ++- squirrelbattle/game.py | 10 ++-- squirrelbattle/interfaces.py | 26 +++++++--- 5 files changed, 100 insertions(+), 17 deletions(-) diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index e4c181e..449a2b7 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -32,6 +32,7 @@ class TexturePack: SWORD: str TEDDY_BEAR: str TIGER: str + TRUMPET: str WALL: str ASCII_PACK: "TexturePack" @@ -77,6 +78,7 @@ TexturePack.ASCII_PACK = TexturePack( SWORD='\u2020', TEDDY_BEAR='8', TIGER='n', + TRUMPET='/', WALL='#', ) @@ -103,5 +105,6 @@ TexturePack.SQUIRREL_PACK = TexturePack( SWORD='πŸ—‘οΈ', TEDDY_BEAR='🧸', TIGER='πŸ…', + TRUMPET='🎺', WALL='🧱', ) diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index d06f35b..1c94ed8 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -1,8 +1,9 @@ -from ..interfaces import FriendlyEntity, InventoryHolder +from ..interfaces import FriendlyEntity, InventoryHolder, FightingEntity, Map from ..translations import gettext as _ from .player import Player +from .monsters import Monster from .items import Item -from random import choice +from random import choice, shuffle class Merchant(InventoryHolder, FriendlyEntity): @@ -11,7 +12,7 @@ class Merchant(InventoryHolder, FriendlyEntity): """ def keys(self) -> list: """ - Returns a friendly entitie's specific attributes + Returns a friendly entitie's specific attributes. """ return super().keys() + ["inventory", "hazel"] @@ -20,7 +21,6 @@ class Merchant(InventoryHolder, FriendlyEntity): 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())()) @@ -41,7 +41,7 @@ class Merchant(InventoryHolder, FriendlyEntity): class Sunflower(FriendlyEntity): """ - A friendly sunflower + A friendly sunflower. """ def __init__(self, maxhealth: int = 15, *args, **kwargs) -> None: @@ -53,3 +53,64 @@ class Sunflower(FriendlyEntity): Lists all that a sunflower can say to the player. """ return [_("Flower power!!"), _("The sun is warm today")] + +class Familiar(FightingEntity): + """ + A friendly familiar that helps the player defeat monsters. + """ + def __init__(self, maxhealth: int = 25, + *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.target = None + def act(self, p: Player, m : Map) : + """ + By default, the familiar tries to stay at distance at most 2 of the + player and if a monster comes in range 3, it focuses on the monster + and attacks it. + """ + if self.target == None: + self.target = p + if self.target == p: + for entity in m.entities: + if (self.y - entity.y) ** 2 + (self.x - entity.x) ** 2 <= 9 and \ + isinstance(entity, Monster): + self.target = entity + entity.paths = dict() #Allows the paths to be calculated. + break + + + # Familiars move according to a Dijkstra algorithm + # that targets their target. + # If they can not move and are already close to their target, + # they hit, except if their target is the player. + if self.target and (self.y, self.x) in self.target.paths: + # Moves to target player by choosing the best available path + for next_y, next_x in self.target.paths[(self.y, self.x)]: + moved = self.check_move(next_y, next_x, True) + if moved: + break + if self.distance_squared(self.target) <= 1 and \ + not isinstance(self.target, Player): + self.map.logs.add_message(self.hit(self.target)) + if self.target.dead : + self.target.paths = None + self.target = None + break + else: + # Moves in a random direction + # If the direction is not available, tries another one + moves = [self.move_up, self.move_down, + self.move_left, self.move_right] + shuffle(moves) + for move in moves: + if move(): + break + +class Trumpet(Familiar) : + """ + A class of familiars. + """ + def __init__(self, name: str = "trumpet", strength: int = 3, + maxhealth: int = 20, *args, **kwargs) -> None: + super().__init__(name=name, strength=strength, + maxhealth=maxhealth, *args, **kwargs) diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index 5453235..87f643e 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -60,7 +60,12 @@ class Monster(FightingEntity): for move in moves: if move(): break - + def move(self, y: int, x:int) -> None: + """ + Overwrites the move function to recalculate paths. + """ + super().move(y, x) + self.recalculate_paths() class Tiger(Monster): """ diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 35c0b0f..72bbe2f 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -111,16 +111,16 @@ class Game: """ if key == KeyValues.UP: if self.player.move_up(): - self.map.tick() + self.map.tick(self.player) elif key == KeyValues.DOWN: if self.player.move_down(): - self.map.tick() + self.map.tick(self.player) elif key == KeyValues.LEFT: if self.player.move_left(): - self.map.tick() + self.map.tick(self.player) elif key == KeyValues.RIGHT: if self.player.move_right(): - self.map.tick() + self.map.tick(self.player) elif key == KeyValues.INVENTORY: self.state = GameMode.INVENTORY elif key == KeyValues.SPACE: @@ -129,7 +129,7 @@ class Game: # Wait for the direction of the friendly entity self.waiting_for_friendly_key = True elif key == KeyValues.WAIT: - self.map.tick() + self.map.tick(self.player) def handle_friendly_entity_chat(self, key: KeyValues) -> None: """ diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index afe4c44..0fec4d0 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -63,7 +63,10 @@ class Map: """ Registers a new entity in the map. """ - self.entities.append(entity) + if entity.is_familiar() : + self.entities.insert(1,entity) + else : + self.entities.append(entity) entity.map = self def remove_entity(self, entity: "Entity") -> None: @@ -152,12 +155,15 @@ class Map: entity.move(y, x) self.add_entity(entity) - def tick(self) -> None: + def tick(self, p: Any) -> None: """ Triggers all entity events. """ for entity in self.entities: - entity.act(self) + if entity.is_familiar(): + entity.act(p, self) + else : + entity.act(self) def save_state(self) -> dict: """ @@ -296,7 +302,7 @@ class Entity: return self.move(self.y, self.x + 1) if force else \ self.check_move(self.y, self.x + 1, True) - def recalculate_paths(self, max_distance: int = 8) -> None: + def recalculate_paths(self, max_distance: int = 12) -> None: """ Uses Dijkstra algorithm to calculate best paths for other entities to go to this entity. If self.paths is None, does nothing. @@ -386,6 +392,13 @@ class Entity: """ return isinstance(self, FriendlyEntity) + def is_familiar(self) -> bool: + """ + Is this entity a familiar? + """ + from squirrelbattle.entities.friendly import Familiar + return isinstance(self, Familiar) + def is_merchant(self) -> bool: """ Is this entity a merchant? @@ -408,9 +421,10 @@ class Entity: from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart from squirrelbattle.entities.monsters import Tiger, Hedgehog, \ Rabbit, TeddyBear - from squirrelbattle.entities.friendly import Merchant, Sunflower + from squirrelbattle.entities.friendly import Merchant, Sunflower, \ + Trumpet return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear, - Sunflower, Tiger, Merchant] + Sunflower, Tiger, Merchant, Trumpet] @staticmethod def get_all_entity_classes_in_a_dict() -> dict: From e1918ab06696418d6d29bef357b4bd4129a9761e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 18 Dec 2020 17:40:52 +0100 Subject: [PATCH 05/22] tick function takes the player as argument --- squirrelbattle/tests/entities_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index f1ffb16..221fa5f 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -57,17 +57,17 @@ class TestEntities(unittest.TestCase): self.map.add_entity(entity) entity.move(15, 44) # Move randomly - self.map.tick() + self.map.tick(self.player) self.assertFalse(entity.y == 15 and entity.x == 44) # Move to the player entity.move(3, 6) - self.map.tick() + self.map.tick(self.player) self.assertTrue(entity.y == 2 and entity.x == 6) # Rabbit should fight old_health = self.player.health - self.map.tick() + self.map.tick(self.player) self.assertTrue(entity.y == 2 and entity.x == 6) self.assertEqual(old_health - entity.strength, self.player.health) self.assertEqual(self.map.logs.messages[-1], From 0394c5d15dde312d32769503322aad029a94cfea Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 18 Dec 2020 17:46:38 +0100 Subject: [PATCH 06/22] Linting --- squirrelbattle/entities/friendly.py | 24 +++++++++++++----------- squirrelbattle/entities/monsters.py | 4 +++- squirrelbattle/entities/player.py | 1 - squirrelbattle/interfaces.py | 17 +++++++++-------- squirrelbattle/settings.py | 3 ++- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 1c94ed8..94b1b8a 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -54,31 +54,32 @@ class Sunflower(FriendlyEntity): """ return [_("Flower power!!"), _("The sun is warm today")] + class Familiar(FightingEntity): """ A friendly familiar that helps the player defeat monsters. """ - def __init__(self, maxhealth: int = 25, + def __init__(self, maxhealth: int = 25, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) + super().__init__(maxhealth=maxhealth, *args, **kwargs) self.target = None - def act(self, p: Player, m : Map) : + + def act(self, p: Player, m: Map) -> None: """ By default, the familiar tries to stay at distance at most 2 of the player and if a monster comes in range 3, it focuses on the monster and attacks it. """ - if self.target == None: + if self.target is None: self.target = p if self.target == p: for entity in m.entities: - if (self.y - entity.y) ** 2 + (self.x - entity.x) ** 2 <= 9 and \ + if (self.y - entity.y) ** 2 + (self.x - entity.x) ** 2 <= 9 and\ isinstance(entity, Monster): self.target = entity - entity.paths = dict() #Allows the paths to be calculated. + entity.paths = dict() # Allows the paths to be calculated. break - - + # Familiars move according to a Dijkstra algorithm # that targets their target. # If they can not move and are already close to their target, @@ -90,9 +91,9 @@ class Familiar(FightingEntity): if moved: break if self.distance_squared(self.target) <= 1 and \ - not isinstance(self.target, Player): + not isinstance(self.target, Player): self.map.logs.add_message(self.hit(self.target)) - if self.target.dead : + if self.target.dead: self.target.paths = None self.target = None break @@ -106,7 +107,8 @@ class Familiar(FightingEntity): if move(): break -class Trumpet(Familiar) : + +class Trumpet(Familiar): """ A class of familiars. """ diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index 87f643e..27c96a6 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -60,13 +60,15 @@ class Monster(FightingEntity): for move in moves: if move(): break - def move(self, y: int, x:int) -> None: + + def move(self, y: int, x: int) -> None: """ Overwrites the move function to recalculate paths. """ super().move(y, x) self.recalculate_paths() + class Tiger(Monster): """ A tiger monster. diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 5f389cf..b5e146b 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later from random import randint -from typing import Dict, Tuple from ..interfaces import FightingEntity, InventoryHolder diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 0fec4d0..75b49ca 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -63,9 +63,9 @@ class Map: """ Registers a new entity in the map. """ - if entity.is_familiar() : - self.entities.insert(1,entity) - else : + if entity.is_familiar(): + self.entities.insert(1, entity) + else: self.entities.append(entity) entity.map = self @@ -100,7 +100,8 @@ class Map: @staticmethod def load(filename: str) -> "Map": """ - Reads a file that contains the content of a map, and builds a Map object. + Reads a file that contains the content of a map, + and builds a Map object. """ with open(filename, "r") as f: file = f.read() @@ -162,7 +163,7 @@ class Map: for entity in self.entities: if entity.is_familiar(): entity.act(p, self) - else : + else: entity.act(self) def save_state(self) -> dict: @@ -307,7 +308,7 @@ class Entity: Uses Dijkstra algorithm to calculate best paths for other entities to go to this entity. If self.paths is None, does nothing. """ - if self.paths == None : + if self.paths is None: return distances = [] predecessors = [] @@ -352,7 +353,7 @@ class Entity: self.paths[(y, x)] = [p for d, p in sorted( [(distances[i][(y, x)], predecessors[i][(y, x)]) for i in range(len(distances)) if (y, x) in predecessors[i]])] - + def act(self, m: Map) -> None: """ Defines the action the entity will do at each tick. @@ -422,7 +423,7 @@ class Entity: from squirrelbattle.entities.monsters import Tiger, Hedgehog, \ Rabbit, TeddyBear from squirrelbattle.entities.friendly import Merchant, Sunflower, \ - Trumpet + Trumpet return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear, Sunflower, Tiger, Merchant, Trumpet] diff --git a/squirrelbattle/settings.py b/squirrelbattle/settings.py index 3ff1be7..58e8cc1 100644 --- a/squirrelbattle/settings.py +++ b/squirrelbattle/settings.py @@ -13,7 +13,8 @@ from .translations import gettext as _ class Settings: """ This class stores the settings of the game. - Settings can be obtained by using for example settings.TEXTURE_PACK directly. + Settings can be obtained by using for example settings.TEXTURE_PACK + directly. The comment can be obtained by using settings.get_comment('TEXTURE_PACK'). We can set the setting by simply using settings.TEXTURE_PACK = 'new_key' """ From b876dab156f1e626ba791026ba5fc3b08dcabae1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 18 Dec 2020 18:13:39 +0100 Subject: [PATCH 07/22] Register Trumpet as savable entity --- squirrelbattle/interfaces.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 75b49ca..33b1466 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -435,7 +435,8 @@ class Entity: from squirrelbattle.entities.player import Player from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \ TeddyBear - from squirrelbattle.entities.friendly import Merchant, Sunflower + from squirrelbattle.entities.friendly import Merchant, Sunflower, \ + Trumpet from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ Heart, Sword return { @@ -450,6 +451,7 @@ class Entity: "Merchant": Merchant, "Sunflower": Sunflower, "Sword": Sword, + "Trumpet": Trumpet, } def save_state(self) -> dict: From ea5f5c142878cb91daba7418b5fe0c7c99e55dac Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 18 Dec 2020 21:30:16 +0100 Subject: [PATCH 08/22] Added an original text art to serve as the project's logo. --- squirrelbattle/assets/ascii-art-ecureuil.txt | 44 ++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 squirrelbattle/assets/ascii-art-ecureuil.txt diff --git a/squirrelbattle/assets/ascii-art-ecureuil.txt b/squirrelbattle/assets/ascii-art-ecureuil.txt new file mode 100644 index 0000000..4266b58 --- /dev/null +++ b/squirrelbattle/assets/ascii-art-ecureuil.txt @@ -0,0 +1,44 @@ + + ━ + ┃ ┃ + ┃ ┃ β–“β–“β–’ β–“β–“ + ┃ ┃ β–“β–“ β–“β–“β–’ + ┃ ┃ β–“β–“β–“ β–“β–“ β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃ ┃ β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃ ┃ β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃ ┃ β–“β–“β–“β–‘β–ˆβ–“β–“β–“β–“β–“β–“β–‘β–ˆβ–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃ ┃ β–“β–“β–“β–“β–‘β–ˆβ–ˆβ–‘β–‘β–“β–“β–‘β–‘β–ˆβ–ˆβ–‘β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ━━▓▓▓▓━━ β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–“β–“β–“β–“ β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–ˆ β–“β–“β–“β–“β–“ β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–„β–„β–„β–„β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–ˆ β–“β–“β–“β–“β–“ β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–“β–“ β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–“β–“β–“β–“β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–“β–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–’β–“β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–“β–“β–“β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’ + β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’ + β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–’β–’β–’ + β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–’ + β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“ + β–“β–“β–“β–“β–“β–“β–“β–“β–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“ + β–“β–“β–“β–“β–“β–“β–“β–“β–“β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“ β–‘ + β–“β–“β–“β–“β–“β–“β–“β–“β–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–“ β–‘β–‘ + β–“β–“β–“β–“β–“β–“β–“β–“β–’β–’β–‘β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–‘β–’β–’β–’β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘ β–‘ + β–“β–“β–“β–“β–“β–“β–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–’β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–‘ β–‘β–‘β–’ + β–‘ β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–’β–’β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’ + β–’β–’β–‘β–‘β–“β–“β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’ β–‘β–‘ + β–’β–’β–’β–’β–’β–“β–’β–’β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ + β–’β–’β–ˆβ–’β–ˆβ–’β–’β–’β–“β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ + β–’β–’β–’β–’β–ˆβ–’β–’β–’β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘ + β–“β–ˆβ–’β–’β–’β–’β–ˆβ–’β–ˆβ–’β–’β–’β–’β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’β–’ + β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘ + β–’β–’β–’β–’β–ˆβ–’β–’β–’β–’β–’β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ + β–’β–’β–ˆβ–’β–’β–’β–’β–’β–‘β–’β–‘β–’β–‘β–‘β–‘β–‘β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘ + β–’β–’β–’β–’β–’β–’β–’β–‘β–’β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘ + β–‘β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–‘β–‘β–‘ β–‘ β–‘β–‘ From 411744bf1052b1dd23b3fdf1789bda6ead56f78f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 18 Dec 2020 22:24:41 +0100 Subject: [PATCH 09/22] Add credits menu, see #42 --- squirrelbattle/display/creditsdisplay.py | 76 +++++++++++++++++++++++ squirrelbattle/display/display_manager.py | 10 ++- squirrelbattle/display/menudisplay.py | 7 ++- squirrelbattle/enums.py | 1 + squirrelbattle/game.py | 2 + 5 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 squirrelbattle/display/creditsdisplay.py diff --git a/squirrelbattle/display/creditsdisplay.py b/squirrelbattle/display/creditsdisplay.py new file mode 100644 index 0000000..453fe8d --- /dev/null +++ b/squirrelbattle/display/creditsdisplay.py @@ -0,0 +1,76 @@ +# Copyright (C) 2020 by ΓΏnΓ©rant, eichhornchen, nicomarg, charlse +# SPDX-License-Identifier: GPL-3.0-or-later + +import curses + +from ..display.display import Box, Display +from ..game import Game +from ..resources import ResourceManager +from ..translations import gettext as _ + + +class CreditsDisplay(Display): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.box = Box(*args, **kwargs) + self.pad = self.newpad(1, 1) + self.ascii_art_displayed = False + + def display(self) -> None: + self.box.refresh(self.y, self.x, self.height, self.width) + self.box.display() + self.pad.erase() + + messages = [ + _("Credits"), + "", + "Squirrel Battle", + "", + _("Developers:"), + "Yohann \"ΓΏnΓ©rant\" D'ANELLO", + "Mathilde \"eichhornchen\" DΓ‰PRΓ‰S", + "Nicolas \"nicomarg\" MARGULIES", + "Charles \"charsle\" PEYRAT", + "", + _("Translators:"), + "Hugo \"ifugao\" JACOB (espaΓ±ol)", + ] + + for i, msg in enumerate(messages): + self.addstr(self.pad, i + (self.height - len(messages)) // 2, + (self.width - len(msg)) // 2, msg, + bold=(i == 0), italic=(":" in msg)) + + if self.ascii_art_displayed: + self.display_ascii_art() + + self.refresh_pad(self.pad, 0, 0, self.y + 1, self.x + 1, + self.height + self.y - 2, + self.width + self.x - 2) + + def display_ascii_art(self): + with open(ResourceManager.get_asset_path("ascii-art-ecureuil.txt"))\ + as f: + ascii_art = f.read().split("\n") + + height, width = len(ascii_art), len(ascii_art[0]) + y_offset, x_offset = (self.height - height) // 2,\ + (self.width - width) // 2 + + for i, line in enumerate(ascii_art): + for j, c in enumerate(line): + bg_color = curses.COLOR_WHITE + fg_color = curses.COLOR_BLACK + if c == 'β–“': + fg_color = (700, 300, 0) + elif c == 'β–’': + fg_color = (700, 300, 0) + bg_color = curses.COLOR_BLACK + elif c == 'β–‘': + fg_color = (350, 150, 0) + self.addstr(self.pad, y_offset + i, x_offset + j, c, + fg_color, bg_color) + + def handle_click(self, y: int, x: int, game: Game) -> None: + if self.pad.inch(y, x) != ord(" "): + self.ascii_art_displayed = True diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index b9d819c..d87ed9d 100644 --- a/squirrelbattle/display/display_manager.py +++ b/squirrelbattle/display/display_manager.py @@ -2,6 +2,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later import curses + +from squirrelbattle.display.creditsdisplay import CreditsDisplay from squirrelbattle.display.display import VerticalSplit, HorizontalSplit, \ Display from squirrelbattle.display.mapdisplay import MapDisplay @@ -30,14 +32,15 @@ class DisplayManager: self.mainmenudisplay = MainMenuDisplay(self.game.main_menu, screen, pack) self.settingsmenudisplay = SettingsMenuDisplay(screen, pack) - self.messagedisplay = MessageDisplay(screen=screen, pack=None) + self.messagedisplay = MessageDisplay(screen, pack) self.hbar = HorizontalSplit(screen, pack) self.vbar = VerticalSplit(screen, pack) + self.creditsdisplay = CreditsDisplay(screen, pack) self.displays = [self.statsdisplay, self.mapdisplay, self.mainmenudisplay, self.settingsmenudisplay, self.logsdisplay, self.messagedisplay, self.playerinventorydisplay, - self.storeinventorydisplay] + self.storeinventorydisplay, self.creditsdisplay] self.update_game_components() def handle_display_action(self, action: DisplayActions, *params) -> None: @@ -123,6 +126,9 @@ class DisplayManager: elif self.game.state == GameMode.SETTINGS: self.settingsmenudisplay.refresh(0, 0, self.rows, self.cols) displays.append(self.settingsmenudisplay) + elif self.game.state == GameMode.CREDITS: + self.creditsdisplay.refresh(0, 0, self.rows, self.cols) + displays.append(self.creditsdisplay) if self.game.message: height, width = 0, 0 diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index 06bae1d..84a20ff 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -7,7 +7,7 @@ from typing import List from squirrelbattle.menus import Menu, MainMenu from .display import Box, Display -from ..enums import KeyValues +from ..enums import KeyValues, GameMode from ..game import Game from ..resources import ResourceManager from ..translations import gettext as _ @@ -113,6 +113,8 @@ class MainMenuDisplay(Display): self.addstr(self.pad, 4 + i, max(self.width // 2 - len(self.title[0]) // 2 - 1, 0), self.title[i], self.fg_color) + msg = _("Credits") + self.addstr(self.pad, self.height - 1, self.width - 1 - len(msg), msg) self.refresh_pad(self.pad, 0, 0, self.y, self.x, self.height + self.y - 1, self.width + self.x - 1) @@ -133,6 +135,9 @@ class MainMenuDisplay(Display): if y <= len(self.title): self.fg_color = randint(0, 1000), randint(0, 1000), randint(0, 1000) + if y == self.height - 1 and x >= self.width - 1 - len(_("Credits")): + game.state = GameMode.CREDITS + class PlayerInventoryDisplay(MenuDisplay): """ diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index d9b0735..2e1dd64 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -28,6 +28,7 @@ class GameMode(Enum): SETTINGS = auto() INVENTORY = auto() STORE = auto() + CREDITS = auto() class KeyValues(Enum): diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 72bbe2f..03dbfe7 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -103,6 +103,8 @@ class Game: self.settings_menu.handle_key_pressed(key, raw_key, self) elif self.state == GameMode.STORE: self.handle_key_pressed_store(key) + elif self.state == GameMode.CREDITS: + self.state = GameMode.MAINMENU self.display_actions(DisplayActions.REFRESH) def handle_key_pressed_play(self, key: KeyValues) -> None: From 4b174f26e4d961e6ff3d72ea3472b4859efa6637 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 18 Dec 2020 23:36:08 +0100 Subject: [PATCH 10/22] Better colors --- squirrelbattle/assets/ascii-art-ecureuil.txt | 34 ++++++++++---------- squirrelbattle/display/creditsdisplay.py | 24 ++++++++++++-- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/squirrelbattle/assets/ascii-art-ecureuil.txt b/squirrelbattle/assets/ascii-art-ecureuil.txt index 4266b58..bd56744 100644 --- a/squirrelbattle/assets/ascii-art-ecureuil.txt +++ b/squirrelbattle/assets/ascii-art-ecureuil.txt @@ -1,17 +1,17 @@ - ━ - ┃ ┃ - ┃ ┃ β–“β–“β–’ β–“β–“ - ┃ ┃ β–“β–“ β–“β–“β–’ - ┃ ┃ β–“β–“β–“ β–“β–“ β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’ - ┃ ┃ β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - ┃ ┃ β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - ┃ ┃ β–“β–“β–“β–‘β–ˆβ–“β–“β–“β–“β–“β–“β–‘β–ˆβ–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - ┃ ┃ β–“β–“β–“β–“β–‘β–ˆβ–ˆβ–‘β–‘β–“β–“β–‘β–‘β–ˆβ–ˆβ–‘β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - ━━▓▓▓▓━━ β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β‹€ + ┃|┃ + ┃|┃ β–“β–“β–’ β–“β–“ + ┃|┃ β–“β–“ β–“β–“β–’ + ┃|┃ β–“β–“β–“ β–“β–“ β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃|┃ β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃|┃ β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃|┃ β–“β–“β–“β–¬β–ˆβ–“β–“β–“β–“β–“β–“β–¬β–ˆβ–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃|┃ β–“β–“β–“β–“β–‘β–ˆβ–ˆβ–‘β–‘β–“β–“β–‘β–‘β–ˆβ–ˆβ–‘β–“β–“β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ━━▓▓▓▓━━ β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–“β–“β–“β–“β–“β–“ β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - β–ˆ β–“β–“β–“β–“β–“ β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–„β–„β–„β–„β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ - β–ˆ β–“β–“β–“β–“β–“ β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃ β–“β–“β–“β–“β–“ β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–„β–„β–„β–„β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + ┃ β–“β–“β–“β–“β–“ β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–“β–“β–“β–“ β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–“β–“β–“β–“β–“β–“β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–“β–“β–“β–“β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ @@ -34,11 +34,11 @@ β–‘ β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–’β–’β–’β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’ β–’β–’β–‘β–‘β–“β–“β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’ β–‘β–‘ β–’β–’β–’β–’β–’β–“β–’β–’β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ - β–’β–’β–ˆβ–’β–ˆβ–’β–’β–’β–“β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ - β–’β–’β–’β–’β–ˆβ–’β–’β–’β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘ - β–“β–ˆβ–’β–’β–’β–’β–ˆβ–’β–ˆβ–’β–’β–’β–’β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’β–’ + β–’β–’β–ˆβ–’β–ˆβ–’β–’β–’β–“β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ + β–’β–’β–’β–’β–ˆβ–’β–’β–’β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘ + β–“β–ˆβ–’β–’β–’β–’β–ˆβ–’β–ˆβ–’β–’β–’β–’β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’β–’ β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘ - β–’β–’β–’β–’β–ˆβ–’β–’β–’β–’β–’β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ - β–’β–’β–ˆβ–’β–’β–’β–’β–’β–‘β–’β–‘β–’β–‘β–‘β–‘β–‘β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘ + β–’β–’β–’β–’β–ˆβ–’β–’β–’β–’β–’β–’β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ + β–’β–’β–ˆβ–’β–’β–’β–’β–’β–‘β–’β–‘β–’β–‘β–‘β–‘β–‘β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘ β–’β–’β–’β–’β–’β–’β–’β–‘β–’β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ β–‘ β–‘β–“β–“β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–’β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–“β–“β–‘β–‘β–‘ β–‘ β–‘β–‘ diff --git a/squirrelbattle/display/creditsdisplay.py b/squirrelbattle/display/creditsdisplay.py index 453fe8d..c3bbe10 100644 --- a/squirrelbattle/display/creditsdisplay.py +++ b/squirrelbattle/display/creditsdisplay.py @@ -61,16 +61,34 @@ class CreditsDisplay(Display): for j, c in enumerate(line): bg_color = curses.COLOR_WHITE fg_color = curses.COLOR_BLACK - if c == 'β–“': + bold = False + if c == ' ': + bg_color = curses.COLOR_BLACK + elif c == '━' or c == '┃' or c == 'β‹€': + bold = True + fg_color = curses.COLOR_WHITE + bg_color = curses.COLOR_BLACK + elif c == '|': + bold = True # c = '┃' + fg_color = (100, 700, 1000) + bg_color = curses.COLOR_BLACK + elif c == 'β–“': fg_color = (700, 300, 0) elif c == 'β–’': fg_color = (700, 300, 0) bg_color = curses.COLOR_BLACK elif c == 'β–‘': fg_color = (350, 150, 0) + elif c == 'β–ˆ': + fg_color = (0, 0, 0) + bg_color = curses.COLOR_BLACK + elif c == 'β–¬': + c = 'β–ˆ' + fg_color = (1000, 1000, 1000) + bg_color = curses.COLOR_BLACK self.addstr(self.pad, y_offset + i, x_offset + j, c, - fg_color, bg_color) + fg_color, bg_color, bold=bold) def handle_click(self, y: int, x: int, game: Game) -> None: - if self.pad.inch(y, x) != ord(" "): + if self.pad.inch(y - 1, x - 1) != ord(" "): self.ascii_art_displayed = True From ed6457e94d0f6b9c32e8bd6e10d5c9e7fb7ee5cc Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 18 Dec 2020 23:39:56 +0100 Subject: [PATCH 11/22] Test credits menu --- squirrelbattle/tests/game_test.py | 11 +++++++++++ squirrelbattle/tests/screen.py | 3 +++ 2 files changed, 14 insertions(+) diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 750335f..40a2331 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -585,3 +585,14 @@ class TestGame(unittest.TestCase): # Exit the menu self.game.handle_key_pressed(KeyValues.SPACE) self.assertEqual(self.game.state, GameMode.PLAY) + + def test_credits(self) -> None: + """ + Load credits menu. + """ + self.game.state = GameMode.MAINMENU + + self.game.display_actions(DisplayActions.MOUSE, 41, 41) + self.assertEqual(self.game.state, GameMode.CREDITS) + self.game.display_actions(DisplayActions.MOUSE, 21, 21) + self.game.display_actions(DisplayActions.REFRESH) diff --git a/squirrelbattle/tests/screen.py b/squirrelbattle/tests/screen.py index 9a8afe6..549fdc1 100644 --- a/squirrelbattle/tests/screen.py +++ b/squirrelbattle/tests/screen.py @@ -24,3 +24,6 @@ class FakePad: def getmaxyx(self) -> Tuple[int, int]: return 42, 42 + + def inch(self, y, x) -> str: + return "i" From 505e0a4efbf38dc27e3dd4c3f104c491bdd1c63e Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Mon, 21 Dec 2020 14:23:58 +0100 Subject: [PATCH 12/22] Fixes issue #54, repaired the attribution of the familiars' target --- squirrelbattle/entities/friendly.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 94b1b8a..ff40d8b 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -55,7 +55,7 @@ class Sunflower(FriendlyEntity): return [_("Flower power!!"), _("The sun is warm today")] -class Familiar(FightingEntity): +class Familiar(FriendlyEntity): #FightingEntity """ A friendly familiar that helps the player defeat monsters. """ @@ -64,6 +64,13 @@ class Familiar(FightingEntity): super().__init__(maxhealth=maxhealth, *args, **kwargs) self.target = None +## @property +## def dialogue_option(self) -> list: +## """ +## Debug function (to see if used in the real game) +## """ +## return [_("My target is"+str(self.target))] + def act(self, p: Player, m: Map) -> None: """ By default, the familiar tries to stay at distance at most 2 of the @@ -71,10 +78,16 @@ class Familiar(FightingEntity): and attacks it. """ if self.target is None: + #If the previous target is dead(or if there was no previous target) + #the familiar tries to get closer to the player. + self.target = p + elif self.target.dead: self.target = p if self.target == p: + #Look for monsters around the player to kill TOFIX : if monster is out + #of range, continue targetting player. for entity in m.entities: - if (self.y - entity.y) ** 2 + (self.x - entity.x) ** 2 <= 9 and\ + if (p.y - entity.y) ** 2 + (p.x - entity.x) ** 2 <= 9 and\ isinstance(entity, Monster): self.target = entity entity.paths = dict() # Allows the paths to be calculated. @@ -93,9 +106,6 @@ class Familiar(FightingEntity): if self.distance_squared(self.target) <= 1 and \ not isinstance(self.target, Player): self.map.logs.add_message(self.hit(self.target)) - if self.target.dead: - self.target.paths = None - self.target = None break else: # Moves in a random direction From de3aba396de797edbee9fbd997fbcd3004aad241 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Thu, 31 Dec 2020 14:51:17 +0100 Subject: [PATCH 13/22] Re-changed familiar's inheritance from friendlyEntity to FightingEntity (just a leftover from debug) --- squirrelbattle/entities/friendly.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index ff40d8b..fb33a3f 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -1,4 +1,4 @@ -from ..interfaces import FriendlyEntity, InventoryHolder, FightingEntity, Map +from ..interfaces import FriendlyEntity, InventoryHolder, Map, FightingEntity from ..translations import gettext as _ from .player import Player from .monsters import Monster @@ -55,7 +55,7 @@ class Sunflower(FriendlyEntity): return [_("Flower power!!"), _("The sun is warm today")] -class Familiar(FriendlyEntity): #FightingEntity +class Familiar(FightingEntity): """ A friendly familiar that helps the player defeat monsters. """ From 7cd4721daa14a2a66c4bac02d8c6ee99c539c2d6 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Thu, 31 Dec 2020 15:00:20 +0100 Subject: [PATCH 14/22] linting --- squirrelbattle/display/creditsdisplay.py | 2 +- squirrelbattle/entities/friendly.py | 20 ++++++++++---------- squirrelbattle/tests/screen.py | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/squirrelbattle/display/creditsdisplay.py b/squirrelbattle/display/creditsdisplay.py index c3bbe10..af8d879 100644 --- a/squirrelbattle/display/creditsdisplay.py +++ b/squirrelbattle/display/creditsdisplay.py @@ -48,7 +48,7 @@ class CreditsDisplay(Display): self.height + self.y - 2, self.width + self.x - 2) - def display_ascii_art(self): + def display_ascii_art(self) -> None: with open(ResourceManager.get_asset_path("ascii-art-ecureuil.txt"))\ as f: ascii_art = f.read().split("\n") diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index fb33a3f..974fe1f 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -64,12 +64,12 @@ class Familiar(FightingEntity): super().__init__(maxhealth=maxhealth, *args, **kwargs) self.target = None -## @property -## def dialogue_option(self) -> list: -## """ -## Debug function (to see if used in the real game) -## """ -## return [_("My target is"+str(self.target))] +# @property +# def dialogue_option(self) -> list: +# """ +# Debug function (to see if used in the real game) +# """ +# return [_("My target is"+str(self.target))] def act(self, p: Player, m: Map) -> None: """ @@ -78,14 +78,14 @@ class Familiar(FightingEntity): and attacks it. """ if self.target is None: - #If the previous target is dead(or if there was no previous target) - #the familiar tries to get closer to the player. + # If the previous target is dead(or if there was no previous target) + # the familiar tries to get closer to the player. self.target = p elif self.target.dead: self.target = p if self.target == p: - #Look for monsters around the player to kill TOFIX : if monster is out - #of range, continue targetting player. + # Look for monsters around the player to kill TOFIX : if monster is + # out of range, continue targetting player. for entity in m.entities: if (p.y - entity.y) ** 2 + (p.x - entity.x) ** 2 <= 9 and\ isinstance(entity, Monster): diff --git a/squirrelbattle/tests/screen.py b/squirrelbattle/tests/screen.py index 549fdc1..386b3d3 100644 --- a/squirrelbattle/tests/screen.py +++ b/squirrelbattle/tests/screen.py @@ -25,5 +25,5 @@ class FakePad: def getmaxyx(self) -> Tuple[int, int]: return 42, 42 - def inch(self, y, x) -> str: + def inch(self, y: int, x: int) -> str: return "i" From 8e0b8d4fee2a2ce4989635a4006d854de59d5371 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Thu, 31 Dec 2020 15:16:40 +0100 Subject: [PATCH 15/22] Added a test for lines 106-107 of game.py --- squirrelbattle/tests/game_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 40a2331..f8a6e93 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -596,3 +596,8 @@ class TestGame(unittest.TestCase): self.assertEqual(self.game.state, GameMode.CREDITS) self.game.display_actions(DisplayActions.MOUSE, 21, 21) self.game.display_actions(DisplayActions.REFRESH) + + self.game.state = GameMode.CREDITS + self.game.handle_key_pressed(KeyValues.ENTER) + + self.assertEqual(self.game.state, GameMode.MAINMENU) From 73952a42f32893b1a27494d3c28d58ccac6ef3be Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Thu, 31 Dec 2020 15:43:38 +0100 Subject: [PATCH 16/22] Added tests for familiars --- squirrelbattle/tests/entities_test.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 221fa5f..5f9d298 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -6,6 +6,7 @@ import unittest from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart, Item, \ Explosion from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, TeddyBear +from squirrelbattle.entities.friendly import Trumpet from squirrelbattle.entities.player import Player from squirrelbattle.interfaces import Entity, Map from squirrelbattle.resources import ResourceManager @@ -89,6 +90,31 @@ class TestEntities(unittest.TestCase): self.assertTrue(entity.dead) self.assertGreaterEqual(self.player.current_xp, 3) + # Test the familiars + fam = Trumpet() + entity = Rabbit() + self.map.add_entity(entity) + self.map.add_entity(fam) + + self.player.move(1,6) + entity.move(2,6) + fam.move(2,7) + + entity.health = 2 + entity.paths = [] + entity.recalculate_paths() + fam.target = entity + self.map.tick(self.player) + self.assertTrue(entity.dead) + + self.player.move(5,5) + fam.move(4,5) + fam.target = self.player + self.player.move_down() + self.map.tick(self.player) + self.assertEqual(fam.y, 5) + self.assertEqual(fam.x, 5) + def test_items(self) -> None: """ Tests some random stuff with items. @@ -219,3 +245,4 @@ class TestEntities(unittest.TestCase): player_state = player.save_state() self.assertEqual(player_state["current_xp"], 10) + From c329150aac067c8f26089a50658abd44133431fe Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Thu, 31 Dec 2020 17:15:24 +0100 Subject: [PATCH 17/22] Linting again --- squirrelbattle/tests/entities_test.py | 13 ++++++------- squirrelbattle/tests/game_test.py | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 5f9d298..341461f 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -96,10 +96,10 @@ class TestEntities(unittest.TestCase): self.map.add_entity(entity) self.map.add_entity(fam) - self.player.move(1,6) - entity.move(2,6) - fam.move(2,7) - + self.player.move(1, 6) + entity.move(2, 6) + fam.move(2, 7) + entity.health = 2 entity.paths = [] entity.recalculate_paths() @@ -107,8 +107,8 @@ class TestEntities(unittest.TestCase): self.map.tick(self.player) self.assertTrue(entity.dead) - self.player.move(5,5) - fam.move(4,5) + self.player.move(5, 5) + fam.move(4, 5) fam.target = self.player self.player.move_down() self.map.tick(self.player) @@ -245,4 +245,3 @@ class TestEntities(unittest.TestCase): player_state = player.save_state() self.assertEqual(player_state["current_xp"], 10) - diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index f8a6e93..8cd816b 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -599,5 +599,5 @@ class TestGame(unittest.TestCase): self.game.state = GameMode.CREDITS self.game.handle_key_pressed(KeyValues.ENTER) - + self.assertEqual(self.game.state, GameMode.MAINMENU) From f821fef43017346a159208208ccb9acfa9ca4f6c Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 5 Jan 2021 09:38:49 +0100 Subject: [PATCH 18/22] added tests --- squirrelbattle/tests/entities_test.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 341461f..7d4981f 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -100,6 +100,7 @@ class TestEntities(unittest.TestCase): entity.move(2, 6) fam.move(2, 7) + #test fighting entity.health = 2 entity.paths = [] entity.recalculate_paths() @@ -107,14 +108,30 @@ class TestEntities(unittest.TestCase): self.map.tick(self.player) self.assertTrue(entity.dead) + #test finding a new target + entity2 = Rabbit() + self.map.add_entity(entity2) + entity2.move(2, 6) + self.map.tick(self.player) + self.assertTrue(fam.target==entity2) + self.map.remove_entity(entity2) + + #test following the player and finding the player as target self.player.move(5, 5) fam.move(4, 5) - fam.target = self.player + fam.target = None self.player.move_down() self.map.tick(self.player) + self.assertTrue(fam.target==self.player) self.assertEqual(fam.y, 5) self.assertEqual(fam.x, 5) + #test random move + fam.move(13, 20) + fam.target=self.player + self.map.tick(self.player) + self.assertTrue(fam.x!=20 or fam.y!=13) + def test_items(self) -> None: """ Tests some random stuff with items. From c378eead74e89ca610c07145b8f1d3229adee299 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 5 Jan 2021 10:11:55 +0100 Subject: [PATCH 19/22] Fixed the game loading tests : removed trumpets from the test. --- squirrelbattle/tests/game_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 8cd816b..3ee00b7 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -41,6 +41,11 @@ class TestGame(unittest.TestCase): bomb.hold(self.game.player) sword.hold(self.game.player) + for entity in self.game.map.entities : + #trumpets change order when they are loaded, so it is unsuitable for simple testing + if entity.name == 'trumpet' : + self.game.map.remove_entity(entity) + # Ensure that merchants can be saved merchant = Merchant() merchant.move(3, 6) From 6341f39fb000675190f8697197a791eed6755668 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 5 Jan 2021 10:20:55 +0100 Subject: [PATCH 20/22] Linting --- squirrelbattle/tests/entities_test.py | 17 ++++++++--------- squirrelbattle/tests/game_test.py | 6 +++--- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 7d4981f..b2272f2 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -95,12 +95,11 @@ class TestEntities(unittest.TestCase): entity = Rabbit() self.map.add_entity(entity) self.map.add_entity(fam) - self.player.move(1, 6) entity.move(2, 6) fam.move(2, 7) - #test fighting + # Test fighting entity.health = 2 entity.paths = [] entity.recalculate_paths() @@ -108,29 +107,29 @@ class TestEntities(unittest.TestCase): self.map.tick(self.player) self.assertTrue(entity.dead) - #test finding a new target + # Test finding a new target entity2 = Rabbit() self.map.add_entity(entity2) entity2.move(2, 6) self.map.tick(self.player) - self.assertTrue(fam.target==entity2) + self.assertTrue(fam.target == entity2) self.map.remove_entity(entity2) - #test following the player and finding the player as target + # Test following the player and finding the player as target self.player.move(5, 5) fam.move(4, 5) fam.target = None self.player.move_down() self.map.tick(self.player) - self.assertTrue(fam.target==self.player) + self.assertTrue(fam.target == self.player) self.assertEqual(fam.y, 5) self.assertEqual(fam.x, 5) - #test random move + # Test random move fam.move(13, 20) - fam.target=self.player + fam.target = self.player self.map.tick(self.player) - self.assertTrue(fam.x!=20 or fam.y!=13) + self.assertTrue(fam.x != 20 or fam.y != 13) def test_items(self) -> None: """ diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 3ee00b7..54e18d6 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -41,9 +41,9 @@ class TestGame(unittest.TestCase): bomb.hold(self.game.player) sword.hold(self.game.player) - for entity in self.game.map.entities : - #trumpets change order when they are loaded, so it is unsuitable for simple testing - if entity.name == 'trumpet' : + for entity in self.game.map.entities: + # trumpets change order when they are loaded, this breaks the test. + if entity.name == 'trumpet': self.game.map.remove_entity(entity) # Ensure that merchants can be saved From bb77dab628dbc6399bce4a39223f4ae5917cff3a Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 5 Jan 2021 10:49:04 +0100 Subject: [PATCH 21/22] Fixed a error induced by the merge: creditsdisplay did not have an update function --- squirrelbattle/display/creditsdisplay.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/squirrelbattle/display/creditsdisplay.py b/squirrelbattle/display/creditsdisplay.py index af8d879..1f8ea32 100644 --- a/squirrelbattle/display/creditsdisplay.py +++ b/squirrelbattle/display/creditsdisplay.py @@ -16,6 +16,9 @@ class CreditsDisplay(Display): self.pad = self.newpad(1, 1) self.ascii_art_displayed = False + def update(self, game: Game) -> None: + return + def display(self) -> None: self.box.refresh(self.y, self.x, self.height, self.width) self.box.display() From 9cb5c9157f1f2b2d246cce77b31f394f17db5e8c Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Tue, 5 Jan 2021 10:59:17 +0100 Subject: [PATCH 22/22] Linting --- squirrelbattle/display/creditsdisplay.py | 2 +- squirrelbattle/display/logsdisplay.py | 1 + squirrelbattle/display/mapdisplay.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/display/creditsdisplay.py b/squirrelbattle/display/creditsdisplay.py index 1f8ea32..a97c10e 100644 --- a/squirrelbattle/display/creditsdisplay.py +++ b/squirrelbattle/display/creditsdisplay.py @@ -17,7 +17,7 @@ class CreditsDisplay(Display): self.ascii_art_displayed = False def update(self, game: Game) -> None: - return + return def display(self) -> None: self.box.refresh(self.y, self.x, self.height, self.width) diff --git a/squirrelbattle/display/logsdisplay.py b/squirrelbattle/display/logsdisplay.py index 434e60b..5c30b41 100644 --- a/squirrelbattle/display/logsdisplay.py +++ b/squirrelbattle/display/logsdisplay.py @@ -12,6 +12,7 @@ class LogsDisplay(Display): """ logs: Logs + def __init__(self, *args) -> None: super().__init__(*args) self.pad = self.newpad(self.rows, self.cols) diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py index 46b85e9..37081cb 100644 --- a/squirrelbattle/display/mapdisplay.py +++ b/squirrelbattle/display/mapdisplay.py @@ -12,6 +12,7 @@ class MapDisplay(Display): """ map: Map + def __init__(self, *args): super().__init__(*args)