From 5736c2300bf1683fee78bb99b3774fe510165bb3 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 11:54:39 +0100 Subject: [PATCH 01/52] Added a scroll object that deals damage based on the player intelligence. Related to #60 --- squirrelbattle/display/texturepack.py | 3 +++ squirrelbattle/entities/items.py | 21 +++++++++++++++++++++ squirrelbattle/interfaces.py | 4 +++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 81302b3..80a20a3 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -34,6 +34,7 @@ class TexturePack: RABBIT: str RING_OF_CRITICAL_DAMAGE: str RING_OF_MORE_EXPERIENCE: str + SCROLL_OF_DAMAGE: str SHIELD: str SUNFLOWER: str SWORD: str @@ -95,6 +96,7 @@ TexturePack.ASCII_PACK = TexturePack( TIGER='n', TRUMPET='/', WALL='#', + SCROLL_OF_DAMAGE=']', ) TexturePack.SQUIRREL_PACK = TexturePack( @@ -131,4 +133,5 @@ TexturePack.SQUIRREL_PACK = TexturePack( TIGER='🐅', TRUMPET='🎺', WALL='🧱', + SCROLL_OF_DAMAGE='📜', ) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 0436e37..6909fc8 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -422,3 +422,24 @@ class RingXP(Ring): experience: float = 2, *args, **kwargs): super().__init__(name=name, price=price, experience=experience, *args, **kwargs) + +class ScrollofDamage(Item): + """ + A scroll that, when used, deals damage to all entities in a certain radius. + """ + + def __init__(self, name: str = "scroll_of_damage", price: int = 18, + *args, **kwargs): + super().__init__(name=name, price=price, *args, **kwargs) + + def use(self) -> None: + """ + Find all entities within a radius of 5, and deal damage based on the + player's intelligence. + """ + for entity in self.held_by.map.entities: + if entity.is_fighting_entity() and not entity == self.held_by: + if entity.distance(self.held_by)<=5: + self.held_by.map.logs.add_message(entity.take_damage(\ + self.held_by, self.held_by.intelligence)) + self.held_by.inventory.remove(self) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index f1f740b..1dff831 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -629,7 +629,8 @@ class Entity: from squirrelbattle.entities.friendly import Merchant, Sunflower, \ Trumpet from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ - Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP + Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \ + ScrollofDamage return { "Tiger": Tiger, "Bomb": Bomb, @@ -649,6 +650,7 @@ class Entity: "Helmet": Helmet, "RingCritical": RingCritical, "RingXP": RingXP, + "ScrollofDamage": ScrollofDamage, } def save_state(self) -> dict: From bde33e9232fafa9b5e2a0828c4708ed3db5df15d Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 16:14:40 +0100 Subject: [PATCH 02/52] Added a second scroll object closes #60 --- squirrelbattle/display/texturepack.py | 3 +++ squirrelbattle/entities/items.py | 25 ++++++++++++++++++++++++- squirrelbattle/entities/monsters.py | 1 + squirrelbattle/interfaces.py | 19 +++++++++++++++++-- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 80a20a3..6ddf461 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -35,6 +35,7 @@ class TexturePack: RING_OF_CRITICAL_DAMAGE: str RING_OF_MORE_EXPERIENCE: str SCROLL_OF_DAMAGE: str + SCROLL_OF_WEAKENING: str SHIELD: str SUNFLOWER: str SWORD: str @@ -97,6 +98,7 @@ TexturePack.ASCII_PACK = TexturePack( TRUMPET='/', WALL='#', SCROLL_OF_DAMAGE=']', + SCROLL_OF_WEAKENING=']', ) TexturePack.SQUIRREL_PACK = TexturePack( @@ -134,4 +136,5 @@ TexturePack.SQUIRREL_PACK = TexturePack( TRUMPET='🎺', WALL='🧱', SCROLL_OF_DAMAGE='📜', + SCROLL_OF_WEAKENING='📜', ) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 6909fc8..740c1bf 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -80,7 +80,8 @@ class Item(Entity): Returns the list of all item classes. """ return [BodySnatchPotion, Bomb, Heart, Shield, Sword,\ - Chestplate, Helmet, RingCritical, RingXP] + Chestplate, Helmet, RingCritical, RingXP, \ + ScrollofDamage, ScrollofWeakening] def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool: """ @@ -443,3 +444,25 @@ class ScrollofDamage(Item): self.held_by.map.logs.add_message(entity.take_damage(\ self.held_by, self.held_by.intelligence)) self.held_by.inventory.remove(self) + +class ScrollofWeakening(Item): + """ + A scroll that, when used, reduces the damage of the ennemies for 3 turn. + """ + + def __init__(self, name: str = "scroll_of_weakening", price: int = 13, + *args, **kwargs): + super().__init__(name=name, price=price, *args, **kwargs) + + def use(self) -> None: + """ + Find all entities and reduce their damage. + """ + for entity in self.held_by.map.entities: + if entity.is_fighting_entity(): #and not entity == self.held_by: + entity.strength = entity.strength - max(1, self.held_by.intelligence//2) + entity.effects.append(["strength", \ + -max(1, self.held_by.intelligence//2), 3]) + self.held_by.map.logs.add_message(\ + _(f"The ennemies have -{max(1, self.held_by.intelligence//2)} strength for 3 turns")) + self.held_by.inventory.remove(self) diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index e22aa51..cae12f2 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -31,6 +31,7 @@ class Monster(FightingEntity): By default, a monster will move randomly where it is possible If the player is closeby, the monster runs to the player. """ + super().act(m) target = None for entity in m.entities: if self.distance_squared(entity) <= 25 and \ diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 1dff831..814736f 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -630,7 +630,7 @@ class Entity: Trumpet from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \ - ScrollofDamage + ScrollofDamage, ScrollofWeakening return { "Tiger": Tiger, "Bomb": Bomb, @@ -651,6 +651,7 @@ class Entity: "RingCritical": RingCritical, "RingXP": RingXP, "ScrollofDamage": ScrollofDamage, + "ScrollofWeakening": ScrollofWeakening, } def save_state(self) -> dict: @@ -693,6 +694,7 @@ class FightingEntity(Entity): self.constitution = constitution self.level = level self.critical = critical + self.effects = [] #effects are temporary buff or weakening of the stats. @property def dead(self) -> bool: @@ -701,13 +703,26 @@ class FightingEntity(Entity): """ return self.health <= 0 + def act(self, m: Map) -> None: + """ + Refreshes all current effects. + """ + for i in range(len(self.effects)): + self.effects[i][2] -= 1 + + l = self.effects[:] + for i in range(len(l)): + if l[i][2] <= 0: + setattr(self, l[i][0], getattr(self, l[i][0])-l[i][1]) + self.effects.remove(l[i]) + def hit(self, opponent: "FightingEntity") -> str: """ The entity deals damage to the opponent based on their respective stats. """ diceroll = randint(1, 100) - damage = self.strength + damage = max(0, self.strength) string = " " if diceroll <= self.critical: # It is a critical hit damage *= 4 From 46a5dc6931d51d2ba15472541294f999c0debeb4 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Fri, 8 Jan 2021 17:26:56 +0100 Subject: [PATCH 03/52] Made mobs check if they can see the player --- squirrelbattle/entities/monsters.py | 4 +++- squirrelbattle/interfaces.py | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index e22aa51..374b1d6 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -42,7 +42,9 @@ class Monster(FightingEntity): # 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: + if target and (self.y, self.x) in target.paths and \ + self.map.is_visible_from(self.y, self.x, + target.y, target.x, 5): # Moves to target player by choosing the best available path for next_y, next_x in target.paths[(self.y, self.x)]: moved = self.check_move(next_y, next_x, True) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 900067c..17e5cee 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -7,6 +7,7 @@ from random import choice, choices, randint from typing import List, Optional, Any, Dict, Tuple from queue import PriorityQueue from functools import reduce +from copy import deepcopy from .display.texturepack import TexturePack from .translations import gettext as _ @@ -193,6 +194,14 @@ class Map: entity.move(y, x) self.add_entity(entity) + def is_visible_from(self, starty: int, startx: int, desty: int, destx: int, + max_range: int) -> bool: + oldvisibility = deepcopy(self.visibility) + self.compute_visibility(starty, startx, max_range) + result = self.visibility[desty][destx] + self.visibility = oldvisibility + return result + def compute_visibility(self, y: int, x: int, max_range: int) -> None: """ Sets the visible tiles to be the ones visible by an entity at point @@ -245,10 +254,12 @@ class Map: if x + y > max_range: continue is_opaque = self.is_wall(y, x, octant, origin) + if y == top_y and octant == 7 and x == 4: + self.logs.add_message(f"{x}, {y}, {top.X}, {top.Y}") is_visible = is_opaque\ - or ((y != top_y or top > Slope(y * 4 - 1, x * 4 + 1)) + or ((y != top_y or top >= Slope(y, x)) and (y != bottom_y - or bottom < Slope(y * 4 + 1, x * 4 - 1))) + or bottom <= Slope(y, x))) # is_visible = is_opaque\ # or ((y != top_y or top >= Slope(y, x)) # and (y != bottom_y or bottom <= Slope(y, x))) From f6210a6356cd0a42b992395e84143bc56a62625a Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 18:06:26 +0100 Subject: [PATCH 04/52] Added a Bow, related to #64 --- squirrelbattle/display/texturepack.py | 6 +++ squirrelbattle/entities/items.py | 60 +++++++++++++++++++++++++-- squirrelbattle/entities/player.py | 5 ++- squirrelbattle/enums.py | 3 ++ squirrelbattle/game.py | 32 ++++++++++++++ squirrelbattle/interfaces.py | 4 +- squirrelbattle/settings.py | 1 + 7 files changed, 105 insertions(+), 6 deletions(-) diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 6ddf461..0ced27f 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -21,6 +21,7 @@ class TexturePack: BODY_SNATCH_POTION: str BOMB: str + BOW: str CHESTPLATE: str EAGLE: str EMPTY: str @@ -34,6 +35,7 @@ class TexturePack: RABBIT: str RING_OF_CRITICAL_DAMAGE: str RING_OF_MORE_EXPERIENCE: str + RULER: str SCROLL_OF_DAMAGE: str SCROLL_OF_WEAKENING: str SHIELD: str @@ -75,6 +77,7 @@ TexturePack.ASCII_PACK = TexturePack( BODY_SNATCH_POTION='S', BOMB='ç', + BOW=')', CHESTPLATE='(', EAGLE='µ', EMPTY=' ', @@ -90,6 +93,7 @@ TexturePack.ASCII_PACK = TexturePack( RABBIT='Y', RING_OF_CRITICAL_DAMAGE='o', RING_OF_MORE_EXPERIENCE='o', + RULER='\\', SHIELD='D', SUNFLOWER='I', SWORD='\u2020', @@ -112,6 +116,7 @@ TexturePack.SQUIRREL_PACK = TexturePack( BODY_SNATCH_POTION='🔀', BOMB='💣', + BOW='🏹', CHESTPLATE='🦺', EAGLE='🦅', EMPTY=' ', @@ -128,6 +133,7 @@ TexturePack.SQUIRREL_PACK = TexturePack( RABBIT='🐇', RING_OF_CRITICAL_DAMAGE='💍', RING_OF_MORE_EXPERIENCE='💍', + RULER='📏', SHIELD='🛡️ ', SUNFLOWER='🌻', SWORD='🗡️ ', diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 740c1bf..b223688 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -40,6 +40,11 @@ class Item(Entity): Indicates what should be done when the item is used. """ + def throw(self, direction: int) -> None: + """ + Indicates what should be done when the item is thrown. + """ + def equip(self) -> None: """ Indicates what should be done when the item is equipped. @@ -81,7 +86,7 @@ class Item(Entity): """ return [BodySnatchPotion, Bomb, Heart, Shield, Sword,\ Chestplate, Helmet, RingCritical, RingXP, \ - ScrollofDamage, ScrollofWeakening] + ScrollofDamage, ScrollofWeakening, Ruler, Bow] def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool: """ @@ -251,6 +256,13 @@ class Sword(Weapon): *args, **kwargs): super().__init__(name=name, price=price, *args, **kwargs) +class Ruler(Weapon): + """ + A basic weapon + """ + def __init__(self, name: str = "ruler", price: int = 2, + damage: int = 1, *args, **kwargs): + super().__init__(name=name, price=price, damage=damage, *args, **kwargs) class Armor(Item): """ @@ -428,7 +440,6 @@ class ScrollofDamage(Item): """ A scroll that, when used, deals damage to all entities in a certain radius. """ - def __init__(self, name: str = "scroll_of_damage", price: int = 18, *args, **kwargs): super().__init__(name=name, price=price, *args, **kwargs) @@ -449,7 +460,6 @@ class ScrollofWeakening(Item): """ A scroll that, when used, reduces the damage of the ennemies for 3 turn. """ - def __init__(self, name: str = "scroll_of_weakening", price: int = 13, *args, **kwargs): super().__init__(name=name, price=price, *args, **kwargs) @@ -459,10 +469,52 @@ class ScrollofWeakening(Item): Find all entities and reduce their damage. """ for entity in self.held_by.map.entities: - if entity.is_fighting_entity(): #and not entity == self.held_by: + if entity.is_fighting_entity() and not entity == self.held_by: entity.strength = entity.strength - max(1, self.held_by.intelligence//2) entity.effects.append(["strength", \ -max(1, self.held_by.intelligence//2), 3]) self.held_by.map.logs.add_message(\ _(f"The ennemies have -{max(1, self.held_by.intelligence//2)} strength for 3 turns")) self.held_by.inventory.remove(self) + +class Bow(Item): + """ + A type of throwable weapon that deals damage based on the player's dexterity. + """ + def __init__(self, name: str = "bow", price: int = 22, damage = 4, + rang = 3, *args, **kwargs): + super().__init__(name=name, price=price, *args, **kwargs) + self.damage = damage + self.range = rang + + def throw(self, direction: int) -> str: + to_kill = None + for entity in self.held_by.map.entities: + if entity.is_fighting_entity(): + if direction == 0 and self.held_by.x == entity.x \ + and self.held_by.y-entity.y>0 and \ + self.held_by.y-entity.y<=self.range: + to_kill = entity + elif direction == 2 and self.held_by.x == entity.x \ + and entity.y-self.held_by.y>0 and \ + entity.y-self.held_by.y<=self.range: + to_kill = entity + elif direction == 1 and self.held_by.y == entity.y \ + and entity.x-self.held_by.x>0 and \ + entity.x-self.held_by.x<=self.range: + to_kill = entity + elif direction == 3 and self.held_by.y == entity.y \ + and self.held_by.x-entity.x>0 and \ + self.held_by.x-entity.x<=self.range: + to_kill = entity + if to_kill: + self.held_by.map.logs.add_message(_("{name} is shot by an arrow.")\ + .format(name=to_kill.translated_name.capitalize())+ " " \ + + to_kill.take_damage(self.held_by, self.damage + self.held_by.dexterity)) + + def equip(self) -> None: + """ + Equip the bow. + """ + self.held_by.remove_from_inventory(self) + self.held_by.equipped_main = self diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 615dfd5..8981341 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -4,7 +4,7 @@ from random import randint from typing import Dict, Optional, Tuple -from .items import Item +from .items import Item, Bow from ..interfaces import FightingEntity, InventoryHolder @@ -38,6 +38,9 @@ class Player(InventoryHolder, FightingEntity): self.max_xp = max_xp self.xp_buff = xp_buff self.inventory = self.translate_inventory(inventory or []) + b = Bow() + b.held_by=self + self.inventory.append(b) self.paths = dict() self.hazel = hazel self.equipped_main = self.dict_to_item(equipped_main) \ diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index 906d6df..cec8a4c 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -48,6 +48,7 @@ class KeyValues(Enum): CHAT = auto() WAIT = auto() LADDER = auto() + LAUNCH = auto() @staticmethod def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]: @@ -84,4 +85,6 @@ class KeyValues(Enum): return KeyValues.WAIT elif key == settings.KEY_LADDER: return KeyValues.LADDER + elif key == settings.KEY_LAUNCH: + return KeyValues.LAUNCH return None diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index ecdfccb..f30183e 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -35,6 +35,7 @@ class Game: """ self.state = GameMode.MAINMENU self.waiting_for_friendly_key = False + self.waiting_for_launch_key = False self.is_in_store_menu = True self.settings = Settings() self.settings.load_settings() @@ -117,6 +118,9 @@ class Game: if self.waiting_for_friendly_key: # The player requested to talk with a friendly entity self.handle_friendly_entity_chat(key) + elif self.waiting_for_launch_key: + # The player requested to launch + self.handle_launch(key) else: self.handle_key_pressed_play(key) elif self.state == GameMode.INVENTORY: @@ -155,6 +159,9 @@ class Game: self.player.equipped_main.use() if self.player.equipped_secondary: self.player.equipped_secondary.use() + elif key == KeyValues.LAUNCH: + # Wait for the direction to launch in + self.waiting_for_launch_key = True elif key == KeyValues.SPACE: self.state = GameMode.MAINMENU elif key == KeyValues.CHAT: @@ -247,6 +254,31 @@ class Game: self.store_menu.update_merchant(entity) self.display_actions(DisplayActions.UPDATE) + def handle_launch(self, key: KeyValues) -> None: + """ + If the player tries to throw something in a direction, the game looks + for entities in that direction and within the range of the player's + weapon and adds damage + """ + if not self.waiting_for_launch_key: + return + self.waiting_for_launch_key = False + + if key == KeyValues.UP: + direction = 0 + elif key == KeyValues.DOWN: + direction = 2 + elif key == KeyValues.LEFT: + direction = 3 + elif key == KeyValues.RIGHT: + direction = 1 + else: + return + + if self.player.equipped_main: + self.player.equipped_main.throw(direction) + + def handle_key_pressed_inventory(self, key: KeyValues) -> None: """ In the inventory menu, we can interact with items or close the menu. diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 814736f..2618892 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -630,7 +630,7 @@ class Entity: Trumpet from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \ - ScrollofDamage, ScrollofWeakening + ScrollofDamage, ScrollofWeakening, Ruler, Bow return { "Tiger": Tiger, "Bomb": Bomb, @@ -650,8 +650,10 @@ class Entity: "Helmet": Helmet, "RingCritical": RingCritical, "RingXP": RingXP, + "Ruler": Ruler, "ScrollofDamage": ScrollofDamage, "ScrollofWeakening": ScrollofWeakening, + "Bow": Bow, } def save_state(self) -> dict: diff --git a/squirrelbattle/settings.py b/squirrelbattle/settings.py index b5e2c14..284b41f 100644 --- a/squirrelbattle/settings.py +++ b/squirrelbattle/settings.py @@ -35,6 +35,7 @@ class Settings: self.KEY_CHAT = ['t', 'Key used to talk to a friendly entity'] self.KEY_WAIT = ['w', 'Key used to wait'] self.KEY_LADDER = ['<', 'Key used to use ladders'] + self.KEY_LAUNCH = ['l', 'Key used to use a bow'] self.TEXTURE_PACK = ['ascii', 'Texture pack'] self.LOCALE = [locale.getlocale()[0][:2], 'Language'] From 903a06c36c1c40204762d09a38e45af05aa3ce17 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 18:38:54 +0100 Subject: [PATCH 05/52] Subclassed and removed some debugging code --- squirrelbattle/entities/items.py | 38 +++++++++++++++++++++++-------- squirrelbattle/entities/player.py | 3 --- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index b223688..8310b81 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -477,13 +477,10 @@ class ScrollofWeakening(Item): _(f"The ennemies have -{max(1, self.held_by.intelligence//2)} strength for 3 turns")) self.held_by.inventory.remove(self) -class Bow(Item): - """ - A type of throwable weapon that deals damage based on the player's dexterity. - """ - def __init__(self, name: str = "bow", price: int = 22, damage = 4, - rang = 3, *args, **kwargs): - super().__init__(name=name, price=price, *args, **kwargs) +class LongRangeWeapon(Item): + def __init__(self, damage: int = 4, + rang: int = 3, *args, **kwargs): + super().__init__(*args, **kwargs) self.damage = damage self.range = rang @@ -510,11 +507,34 @@ class Bow(Item): if to_kill: self.held_by.map.logs.add_message(_("{name} is shot by an arrow.")\ .format(name=to_kill.translated_name.capitalize())+ " " \ - + to_kill.take_damage(self.held_by, self.damage + self.held_by.dexterity)) + + to_kill.take_damage(self.held_by, self.damage + getattr(self.held_by, self.stat))) def equip(self) -> None: """ - Equip the bow. + Equip the weapon. """ self.held_by.remove_from_inventory(self) self.held_by.equipped_main = self + + @property + def stat(self) -> str: + """ + The stat that is used when using the object: dexterity for a bow + or intelligence for a magic staff. + """ + +class Bow(LongRangeWeapon): + """ + A type of long range weapon that deals damage based on the player's dexterity + """ + def __init__(self, name: str = "bow", price: int = 22, damage: int = 4, + rang: int = 3, *args, **kwargs): + super().__init__(name=name, price=price, damage=damage, \ + rang=rang, *args, **kwargs) + + @property + def stat(self) -> str: + """ + Here it is dexterity + """ + return "dexterity" diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 8981341..b4be043 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -38,9 +38,6 @@ class Player(InventoryHolder, FightingEntity): self.max_xp = max_xp self.xp_buff = xp_buff self.inventory = self.translate_inventory(inventory or []) - b = Bow() - b.held_by=self - self.inventory.append(b) self.paths = dict() self.hazel = hazel self.equipped_main = self.dict_to_item(equipped_main) \ From 591630b8a7c9290db35f29ec6302fee276f5d8b6 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 19:05:02 +0100 Subject: [PATCH 06/52] Added a fire ball staff, closes #64 --- squirrelbattle/display/texturepack.py | 3 +++ squirrelbattle/entities/items.py | 39 ++++++++++++++++++++++++--- squirrelbattle/entities/player.py | 2 +- squirrelbattle/interfaces.py | 3 ++- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 0ced27f..3ba64f5 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -25,6 +25,7 @@ class TexturePack: CHESTPLATE: str EAGLE: str EMPTY: str + FIRE_BALL_STAFF: str FLOOR: str HAZELNUT: str HEART: str @@ -82,6 +83,7 @@ TexturePack.ASCII_PACK = TexturePack( EAGLE='µ', EMPTY=' ', EXPLOSION='%', + FIRE_BALL_STAFF=':', FLOOR='.', LADDER='H', HAZELNUT='¤', @@ -121,6 +123,7 @@ TexturePack.SQUIRREL_PACK = TexturePack( EAGLE='🦅', EMPTY=' ', EXPLOSION='💥', + FIRE_BALL_STAFF='🪄', FLOOR='██', LADDER=('🪜', curses.COLOR_WHITE, (1000, 1000, 1000), curses.COLOR_WHITE, (1000, 1000, 1000)), diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 8310b81..9abcfc3 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -86,7 +86,7 @@ class Item(Entity): """ return [BodySnatchPotion, Bomb, Heart, Shield, Sword,\ Chestplate, Helmet, RingCritical, RingXP, \ - ScrollofDamage, ScrollofWeakening, Ruler, Bow] + ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff] def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool: """ @@ -505,9 +505,10 @@ class LongRangeWeapon(Item): self.held_by.x-entity.x<=self.range: to_kill = entity if to_kill: - self.held_by.map.logs.add_message(_("{name} is shot by an arrow.")\ - .format(name=to_kill.translated_name.capitalize())+ " " \ - + to_kill.take_damage(self.held_by, self.damage + getattr(self.held_by, self.stat))) + self.held_by.map.logs.add_message(_("{name}")\ + .format(name=to_kill.translated_name.capitalize())+ self.string + " " \ + + to_kill.take_damage(self.held_by, self.damage + \ + getattr(self.held_by, self.stat))) def equip(self) -> None: """ @@ -523,6 +524,12 @@ class LongRangeWeapon(Item): or intelligence for a magic staff. """ + @property + def string(self) -> str: + """ + The string that is printed when we hit an ennemy. + """ + class Bow(LongRangeWeapon): """ A type of long range weapon that deals damage based on the player's dexterity @@ -538,3 +545,27 @@ class Bow(LongRangeWeapon): Here it is dexterity """ return "dexterity" + + @property + def string(self) -> str: + return " is shot by an arrow." + +class FireBallStaff(LongRangeWeapon): + """ + A type of long range weapon that deals damage based on the player's dexterity + """ + def __init__(self, name: str = "fire_ball_staff", price: int = 36,\ + damage: int = 6, rang: int = 4, *args, **kwargs): + super().__init__(name=name, price=price, damage=damage, \ + rang=rang, *args, **kwargs) + + @property + def stat(self) -> str: + """ + Here it is dexterity + """ + return "intelligence" + + @property + def string(self) -> str: + return " is shot by a fire ball." diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index b4be043..615dfd5 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -4,7 +4,7 @@ from random import randint from typing import Dict, Optional, Tuple -from .items import Item, Bow +from .items import Item from ..interfaces import FightingEntity, InventoryHolder diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 2618892..dc4bdda 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -630,7 +630,7 @@ class Entity: Trumpet from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \ - ScrollofDamage, ScrollofWeakening, Ruler, Bow + ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff return { "Tiger": Tiger, "Bomb": Bomb, @@ -654,6 +654,7 @@ class Entity: "ScrollofDamage": ScrollofDamage, "ScrollofWeakening": ScrollofWeakening, "Bow": Bow, + "FireBallStaff": FireBallStaff, } def save_state(self) -> dict: From 746379bad63a9503e3f38abcd2dbd0701993c609 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 19:18:29 +0100 Subject: [PATCH 07/52] Now with EXPLOSIONS! --- squirrelbattle/entities/items.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 9abcfc3..51a7145 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from random import choice, randint -from typing import Optional +from typing import Optional, Any from ..interfaces import Entity, FightingEntity, Map, InventoryHolder from ..translations import gettext as _ @@ -484,7 +484,7 @@ class LongRangeWeapon(Item): self.damage = damage self.range = rang - def throw(self, direction: int) -> str: + def throw(self, direction: int) -> Any: to_kill = None for entity in self.held_by.map.entities: if entity.is_fighting_entity(): @@ -509,6 +509,7 @@ class LongRangeWeapon(Item): .format(name=to_kill.translated_name.capitalize())+ self.string + " " \ + to_kill.take_damage(self.held_by, self.damage + \ getattr(self.held_by, self.stat))) + return (to_kill.x, to_kill.y) if to_kill else None def equip(self) -> None: """ @@ -569,3 +570,15 @@ class FireBallStaff(LongRangeWeapon): @property def string(self) -> str: return " is shot by a fire ball." + + def throw(self, direction: int) -> None: + """ + Adds an explosion animation when killing something. + """ + A = super().throw(direction) + if A: + x=A[0] + y=A[1] + + explosion = Explosion(y=y, x=x) + self.held_by.map.add_entity(explosion) From 9ff615a6b0a133d31f16764f1e4911ac39804c21 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 22:25:00 +0100 Subject: [PATCH 08/52] Linting and tests... --- squirrelbattle/entities/items.py | 79 +++++++++++++--------- squirrelbattle/game.py | 1 - squirrelbattle/interfaces.py | 17 ++--- squirrelbattle/tests/game_test.py | 108 ++++++++++++++++++++++++++++-- 4 files changed, 160 insertions(+), 45 deletions(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 51a7145..04db192 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -84,8 +84,8 @@ class Item(Entity): """ Returns the list of all item classes. """ - return [BodySnatchPotion, Bomb, Heart, Shield, Sword,\ - Chestplate, Helmet, RingCritical, RingXP, \ + return [BodySnatchPotion, Bomb, Heart, Shield, Sword, + Chestplate, Helmet, RingCritical, RingXP, ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff] def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool: @@ -256,13 +256,15 @@ class Sword(Weapon): *args, **kwargs): super().__init__(name=name, price=price, *args, **kwargs) + class Ruler(Weapon): """ A basic weapon """ def __init__(self, name: str = "ruler", price: int = 2, damage: int = 1, *args, **kwargs): - super().__init__(name=name, price=price, damage=damage, *args, **kwargs) + super().__init__(name=name, price=price, damage=damage, *args, **kwargs) + class Armor(Item): """ @@ -297,6 +299,7 @@ class Shield(Armor): super().__init__(name=name, constitution=constitution, price=price, *args, **kwargs) + class Helmet(Armor): """ Class of helmet items, they can be equipped on the head. @@ -312,6 +315,7 @@ class Helmet(Armor): self.held_by.remove_from_inventory(self) self.held_by.equipped_helmet = self + class Chestplate(Armor): """ Class of chestplate items, they can be equipped on the body. @@ -327,6 +331,7 @@ class Chestplate(Armor): self.held_by.remove_from_inventory(self) self.held_by.equipped_armor = self + class BodySnatchPotion(Item): """ The body-snatch potion allows to exchange all characteristics with a random @@ -436,6 +441,7 @@ class RingXP(Ring): super().__init__(name=name, price=price, experience=experience, *args, **kwargs) + class ScrollofDamage(Item): """ A scroll that, when used, deals damage to all entities in a certain radius. @@ -451,11 +457,12 @@ class ScrollofDamage(Item): """ for entity in self.held_by.map.entities: if entity.is_fighting_entity() and not entity == self.held_by: - if entity.distance(self.held_by)<=5: - self.held_by.map.logs.add_message(entity.take_damage(\ + if entity.distance(self.held_by) <= 5: + self.held_by.map.logs.add_message(entity.take_damage( self.held_by, self.held_by.intelligence)) self.held_by.inventory.remove(self) + class ScrollofWeakening(Item): """ A scroll that, when used, reduces the damage of the ennemies for 3 turn. @@ -470,13 +477,17 @@ class ScrollofWeakening(Item): """ for entity in self.held_by.map.entities: if entity.is_fighting_entity() and not entity == self.held_by: - entity.strength = entity.strength - max(1, self.held_by.intelligence//2) - entity.effects.append(["strength", \ - -max(1, self.held_by.intelligence//2), 3]) - self.held_by.map.logs.add_message(\ - _(f"The ennemies have -{max(1, self.held_by.intelligence//2)} strength for 3 turns")) + entity.strength = entity.strength - \ + max(1, self.held_by.intelligence // 2) + entity.effects.append(["strength", + -max(1, self.held_by.intelligence // 2), + 3]) + self.held_by.map.logs.add_message( + _(f"The ennemies have -{max(1, self.held_by.intelligence // 2)}" + + "strength for 3 turns")) self.held_by.inventory.remove(self) + class LongRangeWeapon(Item): def __init__(self, damage: int = 4, rang: int = 3, *args, **kwargs): @@ -489,26 +500,28 @@ class LongRangeWeapon(Item): for entity in self.held_by.map.entities: if entity.is_fighting_entity(): if direction == 0 and self.held_by.x == entity.x \ - and self.held_by.y-entity.y>0 and \ - self.held_by.y-entity.y<=self.range: + and self.held_by.y - entity.y > 0 and \ + self.held_by.y - entity.y <= self.range: to_kill = entity elif direction == 2 and self.held_by.x == entity.x \ - and entity.y-self.held_by.y>0 and \ - entity.y-self.held_by.y<=self.range: + and entity.y - self.held_by.y > 0 and \ + entity.y - self.held_by.y <= self.range: to_kill = entity elif direction == 1 and self.held_by.y == entity.y \ - and entity.x-self.held_by.x>0 and \ - entity.x-self.held_by.x<=self.range: + and entity.x - self.held_by.x > 0 and \ + entity.x - self.held_by.x <= self.range: to_kill = entity elif direction == 3 and self.held_by.y == entity.y \ - and self.held_by.x-entity.x>0 and \ - self.held_by.x-entity.x<=self.range: + and self.held_by.x - entity.x > 0 and \ + self.held_by.x - entity.x <= self.range: to_kill = entity if to_kill: - self.held_by.map.logs.add_message(_("{name}")\ - .format(name=to_kill.translated_name.capitalize())+ self.string + " " \ - + to_kill.take_damage(self.held_by, self.damage + \ - getattr(self.held_by, self.stat))) + line = _("{name}").format(name=to_kill.translated_name.capitalize() + ) + self.string + " "\ + + to_kill.take_damage( + self.held_by, self.damage + + getattr(self.held_by, self.stat)) + self.held_by.map.logs.add_message(line) return (to_kill.x, to_kill.y) if to_kill else None def equip(self) -> None: @@ -531,13 +544,15 @@ class LongRangeWeapon(Item): The string that is printed when we hit an ennemy. """ + class Bow(LongRangeWeapon): """ - A type of long range weapon that deals damage based on the player's dexterity + A type of long range weapon that deals damage + based on the player's dexterity """ def __init__(self, name: str = "bow", price: int = 22, damage: int = 4, rang: int = 3, *args, **kwargs): - super().__init__(name=name, price=price, damage=damage, \ + super().__init__(name=name, price=price, damage=damage, rang=rang, *args, **kwargs) @property @@ -551,13 +566,15 @@ class Bow(LongRangeWeapon): def string(self) -> str: return " is shot by an arrow." + class FireBallStaff(LongRangeWeapon): """ - A type of long range weapon that deals damage based on the player's dexterity + A type of powerful long range weapon that deals damage + based on the player's intelligence """ - def __init__(self, name: str = "fire_ball_staff", price: int = 36,\ + def __init__(self, name: str = "fire_ball_staff", price: int = 36, damage: int = 6, rang: int = 4, *args, **kwargs): - super().__init__(name=name, price=price, damage=damage, \ + super().__init__(name=name, price=price, damage=damage, rang=rang, *args, **kwargs) @property @@ -575,10 +592,10 @@ class FireBallStaff(LongRangeWeapon): """ Adds an explosion animation when killing something. """ - A = super().throw(direction) - if A: - x=A[0] - y=A[1] + coord = super().throw(direction) + if coord: + x = coord[0] + y = coord[1] explosion = Explosion(y=y, x=x) self.held_by.map.add_entity(explosion) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index f30183e..dc866f2 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -277,7 +277,6 @@ class Game: if self.player.equipped_main: self.player.equipped_main.throw(direction) - def handle_key_pressed_inventory(self, key: KeyValues) -> None: """ diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index dc4bdda..34a4c90 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -605,7 +605,7 @@ class Entity: from squirrelbattle.entities.monsters import Tiger, Hedgehog, \ Rabbit, TeddyBear, GiantSeaEagle from squirrelbattle.entities.friendly import Merchant, Sunflower, \ - Trumpet + Trumpet return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear, Sunflower, Tiger, Merchant, GiantSeaEagle, Trumpet] @@ -697,7 +697,7 @@ class FightingEntity(Entity): self.constitution = constitution self.level = level self.critical = critical - self.effects = [] #effects are temporary buff or weakening of the stats. + self.effects = [] # effects = temporary buff or weakening of the stats. @property def dead(self) -> bool: @@ -713,12 +713,13 @@ class FightingEntity(Entity): for i in range(len(self.effects)): self.effects[i][2] -= 1 - l = self.effects[:] - for i in range(len(l)): - if l[i][2] <= 0: - setattr(self, l[i][0], getattr(self, l[i][0])-l[i][1]) - self.effects.remove(l[i]) - + copy = self.effects[:] + for i in range(len(copy)): + if copy[i][2] <= 0: + setattr(self, copy[i][0], + getattr(self, copy[i][0]) - copy[i][1]) + self.effects.remove(copy[i]) + def hit(self, opponent: "FightingEntity") -> str: """ The entity deals damage to the opponent diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 0843ae8..6fe4203 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -10,11 +10,12 @@ from ..display.display import Display from ..display.display_manager import DisplayManager from ..entities.friendly import Merchant, Sunflower from ..entities.items import Bomb, Heart, Sword, Explosion, Shield, Helmet, \ - Chestplate, RingCritical -from ..entities.monsters import GiantSeaEagle + Chestplate, RingCritical, Bow, FireBallStaff, ScrollofDamage,\ + ScrollofWeakening +from ..entities.monsters import Rabbit, GiantSeaEagle from ..entities.player import Player -from ..enums import DisplayActions -from ..game import Game, KeyValues, GameMode +from ..enums import DisplayActions, KeyValues, GameMode +from ..game import Game from ..interfaces import Map from ..menus import MainMenuValues from ..resources import ResourceManager @@ -344,7 +345,7 @@ class TestGame(unittest.TestCase): self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a') # Navigate to "texture pack" - for ignored in range(12): + for ignored in range(13): self.game.handle_key_pressed(KeyValues.DOWN) # Change texture pack @@ -756,3 +757,100 @@ class TestGame(unittest.TestCase): self.game.handle_key_pressed(KeyValues.ENTER) self.assertEqual(self.game.state, GameMode.MAINMENU) + + def test_launch(self) -> None: + """ + Use the long range weapons to kill some entities. + """ + self.game.state = GameMode.PLAY + self.game.player.move(2, 6) + + b = Bow() + b.held_by = self.game.player + self.game.player.equipped_main = b + self.assertTrue(self.game.player.equipped_main) + + entity = Rabbit() + entity.health = 1 + self.game.map.add_entity(entity) + entity.move(3, 6) + + self.game.handle_launch(KeyValues.UP) + + self.game.waiting_for_launch_key = True + self.game.handle_key_pressed(KeyValues.CHAT) + + entity = Rabbit() + entity.health = 1 + self.game.map.add_entity(entity) + entity.move(2, 8) + self.game.waiting_for_launch_key = True + self.game.handle_key_pressed(KeyValues.RIGHT) + + entity = Rabbit() + entity.health = 1 + self.game.map.add_entity(entity) + entity.move(2, 5) + self.game.waiting_for_launch_key = True + self.game.handle_key_pressed(KeyValues.LEFT) + + key = "l" + KeyValues.translate_key(key, self.game.settings) + + self.game.handle_key_pressed(KeyValues.LAUNCH) + self.assertTrue(self.game.waiting_for_launch_key) + self.game.handle_key_pressed(KeyValues.DOWN) + + self.assertTrue(entity.dead) + + entity2 = Rabbit() + entity2.health = 1 + self.game.map.add_entity(entity2) + entity2.move(1, 6) + + b = FireBallStaff() + self.game.player.inventory.append(b) + b.held_by = self.game.player + b.equip() + + self.game.handle_key_pressed(KeyValues.LAUNCH) + self.assertTrue(self.game.waiting_for_launch_key) + self.game.handle_key_pressed(KeyValues.UP) + + self.assertTrue(entity2.dead) + + def test_scrolls(self) -> None: + """ + Use the scrolls. + """ + self.game.state = GameMode.PLAY + self.game.player.move(2, 6) + + entity = Rabbit() + self.game.map.add_entity(entity) + entity.move(3, 6) + + entity2 = GiantSeaEagle() + self.game.map.add_entity(entity2) + entity2.move(3, 8) + + scroll1 = ScrollofDamage() + scroll2 = ScrollofWeakening() + self.game.player.inventory.append(scroll1) + self.game.player.inventory.append(scroll2) + scroll1.held_by = self.game.player + scroll2.held_by = self.game.player + + scroll1.use() + self.assertTrue(entity.health != entity.maxhealth) + self.assertTrue(entity2.health != entity2.maxhealth) + + scroll2.use() + self.assertEqual(entity.strength, 0) + self.assertEqual(entity2.strength, 999) + + self.game.map.tick(self.game.player) + self.game.map.tick(self.game.player) + self.game.map.tick(self.game.player) + + self.assertEqual(entity2.effects, []) From 175706b1e486fc48e1e464cf55d8a4d58243a44e Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 22:30:30 +0100 Subject: [PATCH 09/52] Merchants had default maxhealth. --- 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 974fe1f..76a2071 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -17,8 +17,8 @@ class Merchant(InventoryHolder, FriendlyEntity): return super().keys() + ["inventory", "hazel"] def __init__(self, name: str = "merchant", inventory: list = None, - hazel: int = 75, *args, **kwargs): - super().__init__(name=name, *args, **kwargs) + hazel: int = 75, maxhealth = 8, *args, **kwargs): + super().__init__(name=name, maxhealth=maxhealth, *args, **kwargs) self.inventory = self.translate_inventory(inventory or []) self.hazel = hazel if not self.inventory: From bdbf214d8d02d8ecc33d54ae95e2d8188d2d22db Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 23:15:48 +0100 Subject: [PATCH 10/52] Added chests, they are immortal and contain objects the player can take for free. --- squirrelbattle/display/display_manager.py | 23 ++++++++++-- squirrelbattle/display/menudisplay.py | 44 +++++++++++++++++++++-- squirrelbattle/display/texturepack.py | 3 ++ squirrelbattle/entities/friendly.py | 33 +++++++++++++++++ squirrelbattle/entities/items.py | 9 +++-- squirrelbattle/enums.py | 1 + squirrelbattle/game.py | 39 ++++++++++++++++++++ squirrelbattle/interfaces.py | 17 ++++++--- squirrelbattle/menus.py | 22 +++++++++++- 9 files changed, 178 insertions(+), 13 deletions(-) diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index 0042615..0eceb62 100644 --- a/squirrelbattle/display/display_manager.py +++ b/squirrelbattle/display/display_manager.py @@ -10,7 +10,8 @@ from squirrelbattle.display.mapdisplay import MapDisplay from squirrelbattle.display.messagedisplay import MessageDisplay from squirrelbattle.display.statsdisplay import StatsDisplay from squirrelbattle.display.menudisplay import MainMenuDisplay, \ - PlayerInventoryDisplay, StoreInventoryDisplay, SettingsMenuDisplay + PlayerInventoryDisplay, StoreInventoryDisplay, SettingsMenuDisplay, \ + ChestInventoryDisplay from squirrelbattle.display.logsdisplay import LogsDisplay from squirrelbattle.display.texturepack import TexturePack from typing import Any, List @@ -29,6 +30,7 @@ class DisplayManager: self.logsdisplay = LogsDisplay(screen, pack) self.playerinventorydisplay = PlayerInventoryDisplay(screen, pack) self.storeinventorydisplay = StoreInventoryDisplay(screen, pack) + self.chestinventorydisplay = ChestInventoryDisplay(screen, pack) self.mainmenudisplay = MainMenuDisplay(self.game.main_menu, screen, pack) self.settingsmenudisplay = SettingsMenuDisplay(screen, pack) @@ -40,7 +42,8 @@ class DisplayManager: self.mainmenudisplay, self.settingsmenudisplay, self.logsdisplay, self.messagedisplay, self.playerinventorydisplay, - self.storeinventorydisplay, self.creditsdisplay] + self.storeinventorydisplay, self.creditsdisplay, + self.chestinventorydisplay] self.update_game_components() def handle_display_action(self, action: DisplayActions, *params) -> None: @@ -87,7 +90,8 @@ class DisplayManager: if self.game.state == GameMode.PLAY \ or self.game.state == GameMode.INVENTORY \ - or self.game.state == GameMode.STORE: + or self.game.state == GameMode.STORE\ + or self.game.state == GameMode.CHEST: # The map pad has already the good size self.mapdisplay.refresh(0, 0, self.rows * 4 // 5, self.mapdisplay.pack.tile_width @@ -124,6 +128,19 @@ class DisplayManager: pack.tile_width * (2 * self.cols // (5 * pack.tile_width))) displays.append(self.storeinventorydisplay) displays.append(self.playerinventorydisplay) + elif self.game.state == GameMode.CHEST: + self.chestinventorydisplay.refresh( + self.rows // 10, + pack.tile_width * (self.cols // (2 * pack.tile_width)), + 8 * self.rows // 10, + pack.tile_width * (2 * self.cols // (5 * pack.tile_width))) + self.playerinventorydisplay.refresh( + self.rows // 10, + pack.tile_width * (self.cols // (10 * pack.tile_width)), + 8 * self.rows // 10, + pack.tile_width * (2 * self.cols // (5 * pack.tile_width))) + displays.append(self.chestinventorydisplay) + displays.append(self.playerinventorydisplay) elif self.game.state == GameMode.MAINMENU: self.mainmenudisplay.refresh(0, 0, self.rows, self.cols) displays.append(self.mainmenudisplay) diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index cc73010..2e870e2 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -5,7 +5,8 @@ import curses from random import randint from typing import List -from squirrelbattle.menus import Menu, MainMenu, SettingsMenu, StoreMenu +from squirrelbattle.menus import Menu, MainMenu, SettingsMenu, StoreMenu,\ + ChestMenu from .display import Box, Display from ..entities.player import Player from ..enums import KeyValues, GameMode @@ -156,13 +157,16 @@ class PlayerInventoryDisplay(MenuDisplay): player: Player = None selected: bool = True store_mode: bool = False + chest_mode: bool = False def update(self, game: Game) -> None: self.player = game.player self.update_menu(game.inventory_menu) self.store_mode = game.state == GameMode.STORE + self.chest_mode = game.state == GameMode.CHEST self.selected = game.state == GameMode.INVENTORY \ - or (self.store_mode and not game.is_in_store_menu) + or (self.store_mode and not game.is_in_store_menu)\ + or (self.chest_mode and not game.is_in_chest_menu) def update_pad(self) -> None: self.menubox.update_title(_("INVENTORY")) @@ -239,3 +243,39 @@ class StoreInventoryDisplay(MenuDisplay): self.menu.position = max(0, min(len(self.menu.values) - 1, y - 2)) game.is_in_store_menu = True game.handle_key_pressed(KeyValues.ENTER) + +class ChestInventoryDisplay(MenuDisplay): + """ + A class to handle the display of a merchant's inventory. + """ + menu: ChestMenu + selected: bool = False + + def update(self, game: Game) -> None: + self.update_menu(game.chest_menu) + self.selected = game.is_in_chest_menu + + def update_pad(self) -> None: + self.menubox.update_title(_("CHEST")) + for i, item in enumerate(self.menu.values): + rep = self.pack[item.name.upper()] + selection = f"[{rep}]" if i == self.menu.position \ + and self.selected else f" {rep} " + self.addstr(self.pad, i + 1, 0, selection + + " " + item.translated_name.capitalize()) + + @property + def truewidth(self) -> int: + return max(1, self.height if hasattr(self, "height") else 10) + + @property + def trueheight(self) -> int: + return 2 + super().trueheight + + def handle_click(self, y: int, x: int, game: Game) -> None: + """ + We can select a menu item with the mouse. + """ + self.menu.position = max(0, min(len(self.menu.values) - 1, y - 2)) + game.is_in_chest_menu = True + game.handle_key_pressed(KeyValues.ENTER) diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 3ba64f5..328f05e 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -22,6 +22,7 @@ class TexturePack: BODY_SNATCH_POTION: str BOMB: str BOW: str + CHEST: str CHESTPLATE: str EAGLE: str EMPTY: str @@ -79,6 +80,7 @@ TexturePack.ASCII_PACK = TexturePack( BODY_SNATCH_POTION='S', BOMB='ç', BOW=')', + CHEST='□', CHESTPLATE='(', EAGLE='µ', EMPTY=' ', @@ -119,6 +121,7 @@ TexturePack.SQUIRREL_PACK = TexturePack( BODY_SNATCH_POTION='🔀', BOMB='💣', BOW='🏹', + CHEST='🧰', CHESTPLATE='🦺', EAGLE='🦅', EMPTY=' ', diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 76a2071..5ce9de9 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -38,6 +38,39 @@ class Merchant(InventoryHolder, FriendlyEntity): """ self.hazel += hz +class Chest(InventoryHolder, FriendlyEntity): + """ + A class of chest inanimate entities which contain objects. + """ + def __init__(self, name: str = "chest", inventory: list = None, + hazel: int = 0, *args, **kwargs): + super().__init__(name=name, *args, **kwargs) + self.hazel = hazel + self.inventory = self.translate_inventory(inventory or []) + if not self.inventory: + for i in range(3): + self.inventory.append(choice(Item.get_all_items())()) + + def talk_to(self, player: Player) -> str: + """ + This function is used to open the chest's inventory in a menu, + and allows the player to take objects. + """ + return _("You have opened the chest") + + def take_damage(self, attacker: "Entity", amount: int) -> str: + """ + A chest is not living, it can not take damage + """ + return _("It's not really effective") + + @property + def dead(self) -> bool: + """ + Chest can not die + """ + return False + class Sunflower(FriendlyEntity): """ diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 04db192..f1c8002 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -88,13 +88,18 @@ class Item(Entity): Chestplate, Helmet, RingCritical, RingXP, ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff] - def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder) -> bool: + def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder,\ + for_free: bool = False) -> bool: """ Does all necessary actions when an object is to be sold. Is overwritten by some classes that cannot exist in the player's inventory. """ - if buyer.hazel >= self.price: + if for_free: + self.hold(buyer) + seller.remove_from_inventory(self) + return True + elif buyer.hazel >= self.price: self.hold(buyer) seller.remove_from_inventory(self) buyer.change_hazel_balance(-self.price) diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index cec8a4c..f39a57f 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -28,6 +28,7 @@ class GameMode(Enum): SETTINGS = auto() INVENTORY = auto() STORE = auto() + CHEST = auto() CREDITS = auto() diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index dc866f2..65c18c1 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -37,6 +37,7 @@ class Game: self.waiting_for_friendly_key = False self.waiting_for_launch_key = False self.is_in_store_menu = True + self.is_in_chest_menu = True self.settings = Settings() self.settings.load_settings() self.settings.write_settings() @@ -46,6 +47,7 @@ class Game: self.settings_menu.update_values(self.settings) self.inventory_menu = menus.InventoryMenu() self.store_menu = menus.StoreMenu() + self.chest_menu = menus.ChestMenu() self.logs = Logs() self.message = None @@ -131,6 +133,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.CHEST: + self.handle_key_pressed_chest(key) elif self.state == GameMode.CREDITS: self.state = GameMode.MAINMENU self.display_actions(DisplayActions.REFRESH) @@ -253,6 +257,11 @@ class Game: self.is_in_store_menu = True self.store_menu.update_merchant(entity) self.display_actions(DisplayActions.UPDATE) + elif entity.is_chest(): + self.state = GameMode.CHEST + self.is_in_chest_menu = True + self.chest_menu.update_chest(entity) + self.display_actions(DisplayActions.UPDATE) def handle_launch(self, key: KeyValues) -> None: """ @@ -332,6 +341,36 @@ class Game: self.display_actions(DisplayActions.UPDATE) # Ensure that the cursor has a good position menu.position = min(menu.position, len(menu.values) - 1) + + def handle_key_pressed_chest(self, key: KeyValues) -> None: + """ + In a chest menu, we can take or put items or close the menu. + """ + menu = self.chest_menu if self.is_in_chest_menu else self.inventory_menu + + if key == KeyValues.SPACE or key == KeyValues.INVENTORY: + self.state = GameMode.PLAY + elif key == KeyValues.UP: + menu.go_up() + elif key == KeyValues.DOWN: + menu.go_down() + elif key == KeyValues.LEFT: + self.is_in_chest_menu = False + self.display_actions(DisplayActions.UPDATE) + elif key == KeyValues.RIGHT: + self.is_in_chest_menu = True + self.display_actions(DisplayActions.UPDATE) + if menu.values and not self.player.dead: + if key == KeyValues.ENTER: + item = menu.validate() + owner = self.chest_menu.chest if self.is_in_chest_menu \ + else self.player + buyer = self.player if self.is_in_chest_menu \ + else self.chest_menu.chest + flag = item.be_sold(buyer, owner, for_free = True) + self.display_actions(DisplayActions.UPDATE) + # Ensure that the cursor has a good position + menu.position = min(menu.position, len(menu.values) - 1) def handle_key_pressed_main_menu(self, key: KeyValues) -> None: """ diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 34a4c90..0a43b9a 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -589,6 +589,13 @@ class Entity: from squirrelbattle.entities.friendly import Merchant return isinstance(self, Merchant) + def is_chest(self) -> bool: + """ + Is this entity a chest? + """ + from squirrelbattle.entities.friendly import Chest + return isinstance(self, Chest) + @property def translated_name(self) -> str: """ @@ -605,9 +612,9 @@ class Entity: from squirrelbattle.entities.monsters import Tiger, Hedgehog, \ Rabbit, TeddyBear, GiantSeaEagle from squirrelbattle.entities.friendly import Merchant, Sunflower, \ - Trumpet + Trumpet, Chest return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear, - Sunflower, Tiger, Merchant, GiantSeaEagle, Trumpet] + Sunflower, Tiger, Merchant, GiantSeaEagle, Trumpet, Chest] @staticmethod def get_weights() -> list: @@ -615,8 +622,7 @@ class Entity: Returns a weigth list associated to the above function, to be used to spawn random entities with a certain probability. """ - return [3, 5, 6, 5, 5, 5, - 5, 4, 4, 1, 2] + return [3, 5, 6, 5, 5, 5, 5, 4, 3, 1, 2, 4] @staticmethod def get_all_entity_classes_in_a_dict() -> dict: @@ -627,7 +633,7 @@ class Entity: from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit, \ TeddyBear, GiantSeaEagle from squirrelbattle.entities.friendly import Merchant, Sunflower, \ - Trumpet + Trumpet, Chest from squirrelbattle.entities.items import BodySnatchPotion, Bomb, \ Heart, Sword, Shield, Chestplate, Helmet, RingCritical, RingXP, \ ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff @@ -655,6 +661,7 @@ class Entity: "ScrollofWeakening": ScrollofWeakening, "Bow": Bow, "FireBallStaff": FireBallStaff, + "Chest": Chest, } def save_state(self) -> dict: diff --git a/squirrelbattle/menus.py b/squirrelbattle/menus.py index 7732642..92ead1c 100644 --- a/squirrelbattle/menus.py +++ b/squirrelbattle/menus.py @@ -6,7 +6,7 @@ from typing import Any, Optional from .display.texturepack import TexturePack from .entities.player import Player -from .entities.friendly import Merchant +from .entities.friendly import Merchant, Chest from .enums import GameMode, KeyValues, DisplayActions from .settings import Settings from .translations import gettext as _, Translator @@ -158,3 +158,23 @@ class StoreMenu(Menu): Returns the values of the menu. """ return self.merchant.inventory if self.merchant else [] + + +class ChestMenu(Menu): + """ + A special instance of a menu : the menu for the inventory of a chest. + """ + chest: Chest = None + + def update_chest(self, chest: Chest) -> None: + """ + Updates the player. + """ + self.chest = chest + + @property + def values(self) -> list: + """ + Returns the values of the menu. + """ + return self.chest.inventory if self.chest else [] From 2eb42668c838fd9922bb8e2ff5a148b7fd56f32a Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 23:32:47 +0100 Subject: [PATCH 11/52] Linting and tests for chests --- squirrelbattle/display/menudisplay.py | 3 +- squirrelbattle/entities/friendly.py | 8 ++-- squirrelbattle/entities/items.py | 2 +- squirrelbattle/game.py | 4 +- squirrelbattle/tests/game_test.py | 58 ++++++++++++++++++++++++++- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index 2e870e2..b3001ab 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -6,7 +6,7 @@ from random import randint from typing import List from squirrelbattle.menus import Menu, MainMenu, SettingsMenu, StoreMenu,\ - ChestMenu + ChestMenu from .display import Box, Display from ..entities.player import Player from ..enums import KeyValues, GameMode @@ -244,6 +244,7 @@ class StoreInventoryDisplay(MenuDisplay): game.is_in_store_menu = True game.handle_key_pressed(KeyValues.ENTER) + class ChestInventoryDisplay(MenuDisplay): """ A class to handle the display of a merchant's inventory. diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 5ce9de9..91515d2 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -1,4 +1,5 @@ -from ..interfaces import FriendlyEntity, InventoryHolder, Map, FightingEntity +from ..interfaces import Entity, FriendlyEntity, InventoryHolder, \ + Map, FightingEntity from ..translations import gettext as _ from .player import Player from .monsters import Monster @@ -17,7 +18,7 @@ class Merchant(InventoryHolder, FriendlyEntity): return super().keys() + ["inventory", "hazel"] def __init__(self, name: str = "merchant", inventory: list = None, - hazel: int = 75, maxhealth = 8, *args, **kwargs): + hazel: int = 75, maxhealth: int = 8, *args, **kwargs): super().__init__(name=name, maxhealth=maxhealth, *args, **kwargs) self.inventory = self.translate_inventory(inventory or []) self.hazel = hazel @@ -38,6 +39,7 @@ class Merchant(InventoryHolder, FriendlyEntity): """ self.hazel += hz + class Chest(InventoryHolder, FriendlyEntity): """ A class of chest inanimate entities which contain objects. @@ -58,7 +60,7 @@ class Chest(InventoryHolder, FriendlyEntity): """ return _("You have opened the chest") - def take_damage(self, attacker: "Entity", amount: int) -> str: + def take_damage(self, attacker: Entity, amount: int) -> str: """ A chest is not living, it can not take damage """ diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index f1c8002..c4594df 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -88,7 +88,7 @@ class Item(Entity): Chestplate, Helmet, RingCritical, RingXP, ScrollofDamage, ScrollofWeakening, Ruler, Bow, FireBallStaff] - def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder,\ + def be_sold(self, buyer: InventoryHolder, seller: InventoryHolder, for_free: bool = False) -> bool: """ Does all necessary actions when an object is to be sold. diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 65c18c1..b576c68 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -341,7 +341,7 @@ class Game: self.display_actions(DisplayActions.UPDATE) # Ensure that the cursor has a good position menu.position = min(menu.position, len(menu.values) - 1) - + def handle_key_pressed_chest(self, key: KeyValues) -> None: """ In a chest menu, we can take or put items or close the menu. @@ -367,7 +367,7 @@ class Game: else self.player buyer = self.player if self.is_in_chest_menu \ else self.chest_menu.chest - flag = item.be_sold(buyer, owner, for_free = True) + item.be_sold(buyer, owner, for_free=True) self.display_actions(DisplayActions.UPDATE) # Ensure that the cursor has a good position menu.position = min(menu.position, len(menu.values) - 1) diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 6fe4203..a8e9c59 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -8,7 +8,7 @@ import unittest from ..bootstrap import Bootstrap from ..display.display import Display from ..display.display_manager import DisplayManager -from ..entities.friendly import Merchant, Sunflower +from ..entities.friendly import Merchant, Sunflower, Chest from ..entities.items import Bomb, Heart, Sword, Explosion, Shield, Helmet, \ Chestplate, RingCritical, Bow, FireBallStaff, ScrollofDamage,\ ScrollofWeakening @@ -854,3 +854,59 @@ class TestGame(unittest.TestCase): self.game.map.tick(self.game.player) self.assertEqual(entity2.effects, []) + + def test_chests(self) -> None: + """ + Interacts with chests. + """ + self.game.state = GameMode.PLAY + + chest = Chest() + chest.move(2, 6) + self.game.map.add_entity(chest) + chest.inventory.append(FireBallStaff()) + + # Talk to merchant + self.game.handle_key_pressed(KeyValues.CHAT) + self.assertTrue(self.game.waiting_for_friendly_key) + self.game.handle_key_pressed(KeyValues.DOWN) + self.assertFalse(self.game.waiting_for_friendly_key) + self.assertEqual(self.game.state, GameMode.CHEST) + self.assertTrue(self.game.logs.messages) + + # Navigate in the menu + self.game.handle_key_pressed(KeyValues.DOWN) + self.game.handle_key_pressed(KeyValues.DOWN) + self.game.handle_key_pressed(KeyValues.LEFT) + self.assertFalse(self.game.is_in_chest_menu) + self.game.handle_key_pressed(KeyValues.RIGHT) + self.assertTrue(self.game.is_in_chest_menu) + self.game.handle_key_pressed(KeyValues.UP) + self.assertEqual(self.game.chest_menu.position, 1) + + # The second item is not a heart + chest.inventory[1] = sword = Sword() + # Take the second item by clicking on it + item = self.game.chest_menu.validate() + self.assertIn(item, chest.inventory) + self.game.display_actions(DisplayActions.MOUSE, 7, 25) + self.assertIn(item, self.game.player.inventory) + self.assertNotIn(item, chest.inventory) + + # Give an item back + self.game.inventory_menu.position = len(self.game.player.inventory) - 1 + self.game.handle_key_pressed(KeyValues.LEFT) + self.assertFalse(self.game.is_in_chest_menu) + self.assertIn(sword, self.game.player.inventory) + self.assertEqual(self.game.inventory_menu.validate(), sword) + self.game.handle_key_pressed(KeyValues.ENTER) + self.assertNotIn(sword, self.game.player.inventory) + self.assertIn(sword, chest.inventory) + + # Test immortality + self.game.player.hit(chest) + self.assertTrue(not chest.dead) + + # Exit the menu + self.game.handle_key_pressed(KeyValues.SPACE) + self.assertEqual(self.game.state, GameMode.PLAY) From cbe3e226b40f6aeca88c2abb788a4f04ac3c62cd Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 23:51:47 +0100 Subject: [PATCH 12/52] Repaired a merge error, tests now work. --- squirrelbattle/display/menudisplay.py | 2 +- squirrelbattle/tests/game_test.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index 1099f5b..64d69b7 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -275,7 +275,7 @@ class ChestInventoryDisplay(MenuDisplay): def trueheight(self) -> int: return 2 + super().trueheight - def handle_click(self, y: int, x: int, game: Game) -> None: + def handle_click(self, y: int, x: int, attr: int, game: Game) -> None: """ We can select a menu item with the mouse. """ diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 0901067..11dd376 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -897,10 +897,11 @@ class TestGame(unittest.TestCase): # The second item is not a heart chest.inventory[1] = sword = Sword() - # Take the second item by clicking on it + # Take the second item item = self.game.chest_menu.validate() self.assertIn(item, chest.inventory) - self.game.display_actions(DisplayActions.MOUSE, 7, 25) + self.game.display_actions(DisplayActions.MOUSE, 7, 25, + curses.BUTTON1_CLICKED) self.assertIn(item, self.game.player.inventory) self.assertNotIn(item, chest.inventory) From ac4a73b2cbff72a95c640c18ff37515556fb3231 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sat, 9 Jan 2021 00:01:02 +0100 Subject: [PATCH 13/52] Final linting --- squirrelbattle/entities/items.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 2560c21..c231b53 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -474,6 +474,7 @@ class RingXP(Ring): super().__init__(name=name, price=price, experience=experience, *args, **kwargs) + class ScrollofDamage(Item): """ A scroll that, when used, deals damage to all entities in a certain radius. @@ -636,4 +637,4 @@ class FireBallStaff(LongRangeWeapon): class Monocle(Item): def __init__(self, name: str = "monocle", price: int = 10, *args, **kwargs): - super().__init__(name=name, price=price, *args, **kwargs) \ No newline at end of file + super().__init__(name=name, price=price, *args, **kwargs) From 8cb2b2388fbf067774d9df762d4582b57d32ee6c Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 9 Jan 2021 18:42:11 +0100 Subject: [PATCH 14/52] Don't use custom colors on unsupported screens, fixes #67 --- squirrelbattle/display/display.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/squirrelbattle/display/display.py b/squirrelbattle/display/display.py index 17d8c29..55e23a8 100644 --- a/squirrelbattle/display/display.py +++ b/squirrelbattle/display/display.py @@ -124,15 +124,24 @@ class Display: return pad.addstr(y, x, msg, attr) def init_pair(self, number: int, foreground: int, background: int) -> None: + foreground = foreground if self.screen and curses.can_change_color() \ + and foreground < curses.COLORS \ + else curses.COLOR_WHITE + background = background if self.screen and curses.can_change_color() \ + and background < curses.COLORS \ + else curses.COLOR_WHITE return curses.init_pair(number, foreground, background) \ - if self.screen else None + if self.screen and curses.can_change_color() \ + and number < curses.COLOR_PAIRS else None def color_pair(self, number: int) -> int: - return curses.color_pair(number) if self.screen else 0 + return curses.color_pair(number) if self.screen \ + and number < curses.COLOR_PAIRS else 0 def init_color(self, number: int, red: int, green: int, blue: int) -> None: return curses.init_color(number, red, green, blue) \ - if self.screen else None + if self.screen and curses.can_change_color() \ + and number < curses.COLORS else None def resize(self, y: int, x: int, height: int, width: int, resize_pad: bool = True) -> None: From 7bf1789239d87d0169795c15c031e18a1b7e8d31 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Sat, 9 Jan 2021 18:45:55 +0100 Subject: [PATCH 15/52] Removed debug code --- squirrelbattle/interfaces.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 828fe59..8117659 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -254,8 +254,6 @@ class Map: if x + y > max_range: continue is_opaque = self.is_wall(y, x, octant, origin) - if y == top_y and octant == 7 and x == 4: - self.logs.add_message(f"{x}, {y}, {top.X}, {top.Y}") is_visible = is_opaque\ or ((y != top_y or top >= Slope(y, x)) and (y != bottom_y From 391fe6897b23f9da50dc08c6740e2bb8ebc20582 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 10:29:43 +0100 Subject: [PATCH 16/52] Drop unusued english translation file --- .../locale/en/LC_MESSAGES/squirrelbattle.po | 207 ------------------ 1 file changed, 207 deletions(-) delete mode 100644 squirrelbattle/locale/en/LC_MESSAGES/squirrelbattle.po diff --git a/squirrelbattle/locale/en/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/en/LC_MESSAGES/squirrelbattle.po deleted file mode 100644 index c45e893..0000000 --- a/squirrelbattle/locale/en/LC_MESSAGES/squirrelbattle.po +++ /dev/null @@ -1,207 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse -# This file is distributed under the same license as the squirrelbattle package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: squirrelbattle 3.14.1\n" -"Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2020-12-01 17:10+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: squirrelbattle/display/statsdisplay.py:34 -msgid "Inventory:" -msgstr "" - -#: squirrelbattle/display/statsdisplay.py:39 -msgid "YOU ARE DEAD" -msgstr "" - -#: squirrelbattle/interfaces.py:394 squirrelbattle/interfaces.py:398 -#: squirrelbattle/interfaces.py:408 -#, python-brace-format -msgid "{name} hits {opponent}." -msgstr "" - -#: squirrelbattle/interfaces.py:405 squirrelbattle/interfaces.py:410 -#: squirrelbattle/interfaces.py:420 -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "" - -#: squirrelbattle/menus.py:45 squirrelbattle/tests/translations_test.py:14 -#: squirrelbattle/tests/game_test.py:284 squirrelbattle/tests/game_test.py:287 -#: squirrelbattle/tests/translations_test.py:16 -#: squirrelbattle/tests/game_test.py:290 -msgid "New game" -msgstr "" - -#: squirrelbattle/menus.py:46 squirrelbattle/tests/translations_test.py:15 -#: squirrelbattle/tests/translations_test.py:17 -msgid "Resume" -msgstr "" - -#: squirrelbattle/menus.py:47 squirrelbattle/tests/translations_test.py:17 -#: squirrelbattle/tests/translations_test.py:19 -msgid "Save" -msgstr "" - -#: squirrelbattle/menus.py:48 squirrelbattle/tests/translations_test.py:16 -#: squirrelbattle/tests/translations_test.py:18 -msgid "Load" -msgstr "" - -#: squirrelbattle/menus.py:49 squirrelbattle/tests/translations_test.py:18 -#: squirrelbattle/tests/translations_test.py:20 -msgid "Settings" -msgstr "" - -#: squirrelbattle/menus.py:50 squirrelbattle/tests/translations_test.py:19 -#: squirrelbattle/tests/translations_test.py:21 -msgid "Exit" -msgstr "" - -#: squirrelbattle/menus.py:71 -msgid "Back" -msgstr "" - -#: squirrelbattle/game.py:147 squirrelbattle/game.py:148 -msgid "" -"Some keys are missing in your save file.\n" -"Your save seems to be corrupt. It got deleted." -msgstr "" - -#: squirrelbattle/game.py:155 squirrelbattle/game.py:156 -msgid "" -"No player was found on this map!\n" -"Maybe you died?" -msgstr "" - -#: squirrelbattle/game.py:175 squirrelbattle/game.py:176 -msgid "" -"The JSON file is not correct.\n" -"Your save seems corrupted. It got deleted." -msgstr "" - -#: squirrelbattle/settings.py:21 squirrelbattle/tests/translations_test.py:21 -#: squirrelbattle/tests/translations_test.py:25 -#: squirrelbattle/tests/translations_test.py:27 -msgid "Main key to move up" -msgstr "" - -#: squirrelbattle/settings.py:22 squirrelbattle/tests/translations_test.py:23 -#: squirrelbattle/tests/translations_test.py:27 -#: squirrelbattle/tests/translations_test.py:29 -msgid "Secondary key to move up" -msgstr "" - -#: squirrelbattle/settings.py:23 squirrelbattle/tests/translations_test.py:25 -#: squirrelbattle/tests/translations_test.py:29 -#: squirrelbattle/tests/translations_test.py:31 -msgid "Main key to move down" -msgstr "" - -#: squirrelbattle/settings.py:24 squirrelbattle/tests/translations_test.py:27 -#: squirrelbattle/tests/translations_test.py:31 -#: squirrelbattle/tests/translations_test.py:33 -msgid "Secondary key to move down" -msgstr "" - -#: squirrelbattle/settings.py:25 squirrelbattle/tests/translations_test.py:29 -#: squirrelbattle/tests/translations_test.py:33 -#: squirrelbattle/tests/translations_test.py:35 -msgid "Main key to move left" -msgstr "" - -#: squirrelbattle/settings.py:26 squirrelbattle/tests/translations_test.py:31 -#: squirrelbattle/tests/translations_test.py:35 -#: squirrelbattle/tests/translations_test.py:37 -msgid "Secondary key to move left" -msgstr "" - -#: squirrelbattle/settings.py:27 squirrelbattle/tests/translations_test.py:33 -#: squirrelbattle/tests/translations_test.py:37 -#: squirrelbattle/tests/translations_test.py:39 -msgid "Main key to move right" -msgstr "" - -#: squirrelbattle/settings.py:29 squirrelbattle/tests/translations_test.py:35 -#: squirrelbattle/tests/translations_test.py:39 -#: squirrelbattle/tests/translations_test.py:41 -msgid "Secondary key to move right" -msgstr "" - -#: squirrelbattle/settings.py:30 squirrelbattle/tests/translations_test.py:37 -#: squirrelbattle/tests/translations_test.py:41 -#: squirrelbattle/tests/translations_test.py:43 -msgid "Key to validate a menu" -msgstr "" - -#: squirrelbattle/settings.py:31 squirrelbattle/tests/translations_test.py:39 -#: squirrelbattle/tests/translations_test.py:43 -#: squirrelbattle/tests/translations_test.py:45 -msgid "Texture pack" -msgstr "" - -#: squirrelbattle/settings.py:32 squirrelbattle/tests/translations_test.py:40 -#: squirrelbattle/tests/translations_test.py:44 -#: squirrelbattle/tests/translations_test.py:46 -msgid "Language" -msgstr "" - -#: squirrelbattle/interfaces.py:407 squirrelbattle/interfaces.py:412 -#: squirrelbattle/interfaces.py:422 -#, python-brace-format -msgid "{name} dies." -msgstr "" - -#: squirrelbattle/tests/translations_test.py:47 -#: squirrelbattle/tests/translations_test.py:49 -msgid "player" -msgstr "" - -#: squirrelbattle/tests/translations_test.py:49 -#: squirrelbattle/tests/translations_test.py:51 -msgid "tiger" -msgstr "" - -#: squirrelbattle/tests/translations_test.py:50 -#: squirrelbattle/tests/translations_test.py:52 -msgid "hedgehog" -msgstr "" - -#: squirrelbattle/tests/translations_test.py:51 -#: squirrelbattle/tests/translations_test.py:53 -msgid "rabbit" -msgstr "" - -#: squirrelbattle/tests/translations_test.py:52 -#: squirrelbattle/tests/translations_test.py:54 -msgid "teddy bear" -msgstr "" - -#: squirrelbattle/tests/translations_test.py:54 -#: squirrelbattle/tests/translations_test.py:56 -msgid "bomb" -msgstr "" - -#: squirrelbattle/tests/translations_test.py:55 -#: squirrelbattle/tests/translations_test.py:57 -msgid "heart" -msgstr "" - -#: squirrelbattle/entities/friendly.py:31 -msgid "Flower power!!" -msgstr "" - -#: squirrelbattle/entities/friendly.py:31 -msgid "The sun is warm today" -msgstr "" From 94c12541ef0cdbff5c935894a90bee5bd77c9106 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 10:46:17 +0100 Subject: [PATCH 17/52] Happy new year! --- COPYING | 4 ++-- docs/conf.py | 9 ++++++--- main.py | 2 +- setup.py | 2 +- squirrelbattle/__init__.py | 2 +- squirrelbattle/bootstrap.py | 2 +- squirrelbattle/display/__init__.py | 2 +- squirrelbattle/display/creditsdisplay.py | 2 +- squirrelbattle/display/display.py | 2 +- squirrelbattle/display/display_manager.py | 2 +- squirrelbattle/display/logsdisplay.py | 2 +- squirrelbattle/display/mapdisplay.py | 2 +- squirrelbattle/display/menudisplay.py | 2 +- squirrelbattle/display/messagedisplay.py | 2 +- squirrelbattle/display/statsdisplay.py | 2 +- squirrelbattle/display/texturepack.py | 2 +- squirrelbattle/entities/__init__.py | 2 +- squirrelbattle/entities/items.py | 2 +- squirrelbattle/entities/monsters.py | 2 +- squirrelbattle/entities/player.py | 2 +- squirrelbattle/enums.py | 2 +- squirrelbattle/game.py | 2 +- squirrelbattle/interfaces.py | 2 +- squirrelbattle/menus.py | 2 +- squirrelbattle/resources.py | 2 +- squirrelbattle/settings.py | 2 +- squirrelbattle/term_manager.py | 2 +- squirrelbattle/tests/__init__.py | 2 +- squirrelbattle/tests/entities_test.py | 2 +- squirrelbattle/tests/game_test.py | 2 +- squirrelbattle/tests/interfaces_test.py | 2 +- squirrelbattle/tests/screen.py | 2 +- squirrelbattle/tests/settings_test.py | 2 +- squirrelbattle/translations.py | 2 +- 34 files changed, 40 insertions(+), 37 deletions(-) diff --git a/COPYING b/COPYING index 1bc08a5..ce2c363 100644 --- a/COPYING +++ b/COPYING @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Squirrel Battle - Copyright (C) 2020 ÿnérant, eichhornchen, nicomarg, charlse + Copyright (C) 2020-2021 ÿnérant, eichhornchen, nicomarg, charlse This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - Squirrel Battle Copyright (C) 2020 ÿnérant, eichhornchen, nicomarg, charlse + Squirrel Battle Copyright (C) 2020-2021 ÿnérant, eichhornchen, nicomarg, charlse This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/docs/conf.py b/docs/conf.py index 4877d19..0d65dd0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,8 +18,11 @@ # -- Project information ----------------------------------------------------- project = 'Squirrel Battle' -copyright = "2020" -author = "Yohann D'ANELLO,\nMathilde DEPRES,\nNicolas MARGULIES,\nCharles PEYRAT" +copyright = "2020-2021" +author = "Yohann D'ANELLO,\n" \ + "Mathilde DEPRES,\n" \ + "Nicolas MARGULIES,\n" \ + "Charles PEYRAT" # -- General configuration --------------------------------------------------- @@ -57,4 +60,4 @@ html_theme = 'sphinx_rtd_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] \ No newline at end of file +html_static_path = ['_static'] diff --git a/main.py b/main.py index fbbbb35..2f6579a 100755 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import argparse import sys diff --git a/setup.py b/setup.py index 7f39d83..5a2a5e7 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import subprocess diff --git a/squirrelbattle/__init__.py b/squirrelbattle/__init__.py index 1cc6688..7c508cd 100644 --- a/squirrelbattle/__init__.py +++ b/squirrelbattle/__init__.py @@ -1,2 +1,2 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/squirrelbattle/bootstrap.py b/squirrelbattle/bootstrap.py index f041aef..543fe01 100644 --- a/squirrelbattle/bootstrap.py +++ b/squirrelbattle/bootstrap.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from squirrelbattle.game import Game diff --git a/squirrelbattle/display/__init__.py b/squirrelbattle/display/__init__.py index 1cc6688..7c508cd 100644 --- a/squirrelbattle/display/__init__.py +++ b/squirrelbattle/display/__init__.py @@ -1,2 +1,2 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/squirrelbattle/display/creditsdisplay.py b/squirrelbattle/display/creditsdisplay.py index 93f2f72..e005c5b 100644 --- a/squirrelbattle/display/creditsdisplay.py +++ b/squirrelbattle/display/creditsdisplay.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/display/display.py b/squirrelbattle/display/display.py index 55e23a8..9b6e97c 100644 --- a/squirrelbattle/display/display.py +++ b/squirrelbattle/display/display.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index b4153c9..f1b879f 100644 --- a/squirrelbattle/display/display_manager.py +++ b/squirrelbattle/display/display_manager.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/display/logsdisplay.py b/squirrelbattle/display/logsdisplay.py index 5c30b41..1c323af 100644 --- a/squirrelbattle/display/logsdisplay.py +++ b/squirrelbattle/display/logsdisplay.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from squirrelbattle.display.display import Display diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py index 701f33e..2439489 100644 --- a/squirrelbattle/display/mapdisplay.py +++ b/squirrelbattle/display/mapdisplay.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from squirrelbattle.interfaces import Map diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index 64d69b7..628961f 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/display/messagedisplay.py b/squirrelbattle/display/messagedisplay.py index d850500..2b1ec30 100644 --- a/squirrelbattle/display/messagedisplay.py +++ b/squirrelbattle/display/messagedisplay.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/display/statsdisplay.py b/squirrelbattle/display/statsdisplay.py index a60f6a2..e8a151a 100644 --- a/squirrelbattle/display/statsdisplay.py +++ b/squirrelbattle/display/statsdisplay.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index d5c6da1..f124523 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/entities/__init__.py b/squirrelbattle/entities/__init__.py index 1cc6688..7c508cd 100644 --- a/squirrelbattle/entities/__init__.py +++ b/squirrelbattle/entities/__init__.py @@ -1,2 +1,2 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index c231b53..c79a350 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from random import choice, randint diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index b62421a..c654428 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from random import shuffle diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 615dfd5..8257f85 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from random import randint diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index f39a57f..808960c 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from enum import Enum, auto diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 9734144..580b660 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from json import JSONDecodeError diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 8117659..95e2628 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from enum import Enum, auto diff --git a/squirrelbattle/menus.py b/squirrelbattle/menus.py index 92ead1c..8de9547 100644 --- a/squirrelbattle/menus.py +++ b/squirrelbattle/menus.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from enum import Enum diff --git a/squirrelbattle/resources.py b/squirrelbattle/resources.py index b3421db..55facca 100644 --- a/squirrelbattle/resources.py +++ b/squirrelbattle/resources.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from pathlib import Path diff --git a/squirrelbattle/settings.py b/squirrelbattle/settings.py index 6f7cf57..92a8b37 100644 --- a/squirrelbattle/settings.py +++ b/squirrelbattle/settings.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import json diff --git a/squirrelbattle/term_manager.py b/squirrelbattle/term_manager.py index 2e74fff..cd028b5 100644 --- a/squirrelbattle/term_manager.py +++ b/squirrelbattle/term_manager.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/tests/__init__.py b/squirrelbattle/tests/__init__.py index 1cc6688..7c508cd 100644 --- a/squirrelbattle/tests/__init__.py +++ b/squirrelbattle/tests/__init__.py @@ -1,2 +1,2 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 5729032..231bd13 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import random import unittest diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index 11dd376..f6407ad 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import curses diff --git a/squirrelbattle/tests/interfaces_test.py b/squirrelbattle/tests/interfaces_test.py index df1cbea..77ae53a 100644 --- a/squirrelbattle/tests/interfaces_test.py +++ b/squirrelbattle/tests/interfaces_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import unittest diff --git a/squirrelbattle/tests/screen.py b/squirrelbattle/tests/screen.py index 57b7dcc..03ae206 100644 --- a/squirrelbattle/tests/screen.py +++ b/squirrelbattle/tests/screen.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later from typing import Tuple diff --git a/squirrelbattle/tests/settings_test.py b/squirrelbattle/tests/settings_test.py index 65cb25a..c6d696c 100644 --- a/squirrelbattle/tests/settings_test.py +++ b/squirrelbattle/tests/settings_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import unittest diff --git a/squirrelbattle/translations.py b/squirrelbattle/translations.py index df140a2..b4cddfc 100644 --- a/squirrelbattle/translations.py +++ b/squirrelbattle/translations.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later import gettext as gt From d73802933582e942d655e17d5d60f8f0d7876088 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 11:25:53 +0100 Subject: [PATCH 18/52] Fix import order --- squirrelbattle/bootstrap.py | 6 +++--- squirrelbattle/display/display_manager.py | 26 +++++++++++------------ squirrelbattle/display/mapdisplay.py | 2 +- squirrelbattle/display/menudisplay.py | 5 ++--- squirrelbattle/display/statsdisplay.py | 2 +- squirrelbattle/display/texturepack.py | 2 +- squirrelbattle/entities/friendly.py | 16 ++++++++------ squirrelbattle/entities/items.py | 4 ++-- squirrelbattle/enums.py | 2 +- squirrelbattle/game.py | 12 +++++------ squirrelbattle/interfaces.py | 12 +++++------ squirrelbattle/menus.py | 4 ++-- squirrelbattle/tests/entities_test.py | 16 +++++++------- squirrelbattle/tests/game_test.py | 12 +++++------ squirrelbattle/tests/interfaces_test.py | 6 +++--- squirrelbattle/translations.py | 2 +- tox.ini | 1 + 17 files changed, 66 insertions(+), 64 deletions(-) diff --git a/squirrelbattle/bootstrap.py b/squirrelbattle/bootstrap.py index 543fe01..ec89129 100644 --- a/squirrelbattle/bootstrap.py +++ b/squirrelbattle/bootstrap.py @@ -1,9 +1,9 @@ # Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later -from squirrelbattle.game import Game -from squirrelbattle.display.display_manager import DisplayManager -from squirrelbattle.term_manager import TermManager +from .display.display_manager import DisplayManager +from .game import Game +from .term_manager import TermManager class Bootstrap: diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index f1b879f..81cba4f 100644 --- a/squirrelbattle/display/display_manager.py +++ b/squirrelbattle/display/display_manager.py @@ -2,21 +2,19 @@ # 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 -from squirrelbattle.display.messagedisplay import MessageDisplay -from squirrelbattle.display.statsdisplay import StatsDisplay -from squirrelbattle.display.menudisplay import MainMenuDisplay, \ - PlayerInventoryDisplay, StoreInventoryDisplay, SettingsMenuDisplay, \ - ChestInventoryDisplay -from squirrelbattle.display.logsdisplay import LogsDisplay -from squirrelbattle.display.texturepack import TexturePack from typing import Any, List -from squirrelbattle.game import Game, GameMode -from squirrelbattle.enums import DisplayActions + +from .creditsdisplay import CreditsDisplay +from .display import Display, HorizontalSplit, VerticalSplit +from .logsdisplay import LogsDisplay +from .mapdisplay import MapDisplay +from .menudisplay import ChestInventoryDisplay, MainMenuDisplay, \ + PlayerInventoryDisplay, SettingsMenuDisplay, StoreInventoryDisplay +from .messagedisplay import MessageDisplay +from .statsdisplay import StatsDisplay +from .texturepack import TexturePack +from ..enums import DisplayActions +from ..game import Game, GameMode class DisplayManager: diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py index 2439489..80e6ada 100644 --- a/squirrelbattle/display/mapdisplay.py +++ b/squirrelbattle/display/mapdisplay.py @@ -1,9 +1,9 @@ # Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later -from squirrelbattle.interfaces import Map from .display import Display from ..game import Game +from ..interfaces import Map class MapDisplay(Display): diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index 628961f..f86c7c4 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -5,12 +5,11 @@ import curses from random import randint from typing import List -from squirrelbattle.menus import Menu, MainMenu, SettingsMenu, StoreMenu,\ - ChestMenu from .display import Box, Display from ..entities.player import Player -from ..enums import KeyValues, GameMode +from ..enums import GameMode, KeyValues from ..game import Game +from ..menus import ChestMenu, MainMenu, Menu, SettingsMenu, StoreMenu from ..resources import ResourceManager from ..translations import gettext as _ diff --git a/squirrelbattle/display/statsdisplay.py b/squirrelbattle/display/statsdisplay.py index e8a151a..b80fa7b 100644 --- a/squirrelbattle/display/statsdisplay.py +++ b/squirrelbattle/display/statsdisplay.py @@ -3,12 +3,12 @@ import curses +from .display import Display from ..entities.items import Monocle from ..entities.player import Player from ..game import Game from ..interfaces import FightingEntity from ..translations import gettext as _ -from .display import Display class StatsDisplay(Display): diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index f124523..16cad4f 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later import curses -from typing import Any, Union, Tuple +from typing import Any, Tuple, Union class TexturePack: diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 91515d2..36f9db3 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -1,11 +1,15 @@ -from ..interfaces import Entity, FriendlyEntity, InventoryHolder, \ - Map, FightingEntity -from ..translations import gettext as _ -from .player import Player -from .monsters import Monster -from .items import Item +# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse +# SPDX-License-Identifier: GPL-3.0-or-later + from random import choice, shuffle +from .items import Item +from .monsters import Monster +from .player import Player +from ..interfaces import Entity, FightingEntity, FriendlyEntity, \ + InventoryHolder, Map +from ..translations import gettext as _ + class Merchant(InventoryHolder, FriendlyEntity): """ diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index c79a350..94a9e36 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -2,9 +2,9 @@ # SPDX-License-Identifier: GPL-3.0-or-later from random import choice, randint -from typing import Optional, Any +from typing import Any, Optional -from ..interfaces import Entity, FightingEntity, Map, InventoryHolder +from ..interfaces import Entity, FightingEntity, InventoryHolder, Map from ..translations import gettext as _ diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index 808960c..b6b4bcd 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -1,7 +1,7 @@ # Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later -from enum import Enum, auto +from enum import auto, Enum from typing import Optional from squirrelbattle.settings import Settings diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 580b660..56ab6ed 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -1,20 +1,20 @@ # Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later -from json import JSONDecodeError -from random import randint -from typing import Any, Optional, List import curses import json +from json import JSONDecodeError import os +from random import randint import sys +from typing import Any, List, Optional +from . import menus from .entities.player import Player -from .enums import GameMode, KeyValues, DisplayActions -from .interfaces import Map, Logs +from .enums import DisplayActions, GameMode, KeyValues +from .interfaces import Logs, Map from .resources import ResourceManager from .settings import Settings -from . import menus from .translations import gettext as _, Translator diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 95e2628..2280909 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -1,13 +1,13 @@ # Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later -from enum import Enum, auto -from math import ceil, sqrt -from random import choice, choices, randint -from typing import List, Optional, Any, Dict, Tuple -from queue import PriorityQueue -from functools import reduce from copy import deepcopy +from enum import auto, Enum +from functools import reduce +from math import ceil, sqrt +from queue import PriorityQueue +from random import choice, choices, randint +from typing import Any, Dict, List, Optional, Tuple from .display.texturepack import TexturePack from .translations import gettext as _ diff --git a/squirrelbattle/menus.py b/squirrelbattle/menus.py index 8de9547..ad4e4b3 100644 --- a/squirrelbattle/menus.py +++ b/squirrelbattle/menus.py @@ -5,9 +5,9 @@ from enum import Enum from typing import Any, Optional from .display.texturepack import TexturePack +from .entities.friendly import Chest, Merchant from .entities.player import Player -from .entities.friendly import Merchant, Chest -from .enums import GameMode, KeyValues, DisplayActions +from .enums import DisplayActions, GameMode, KeyValues from .settings import Settings from .translations import gettext as _, Translator diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index 231bd13..db32877 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -1,16 +1,16 @@ # Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later + import random import unittest -from squirrelbattle.entities.items import BodySnatchPotion, Bomb, Heart, Item, \ - Explosion -from squirrelbattle.entities.monsters import Tiger, Hedgehog, Rabbit,\ - TeddyBear, GiantSeaEagle -from squirrelbattle.entities.friendly import Trumpet -from squirrelbattle.entities.player import Player -from squirrelbattle.interfaces import Entity, Map -from squirrelbattle.resources import ResourceManager +from ..entities.friendly import Trumpet +from ..entities.items import BodySnatchPotion, Bomb, Explosion, Heart, Item +from ..entities.monsters import GiantSeaEagle, Hedgehog, Rabbit, \ + TeddyBear, Tiger +from ..entities.player import Player +from ..interfaces import Entity, Map +from ..resources import ResourceManager class TestEntities(unittest.TestCase): diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index f6407ad..b9de160 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -8,13 +8,13 @@ import unittest from ..bootstrap import Bootstrap from ..display.display import Display from ..display.display_manager import DisplayManager -from ..entities.friendly import Merchant, Sunflower, Chest -from ..entities.items import Bomb, Heart, Sword, Explosion, Shield, Helmet, \ - Chestplate, RingCritical, Bow, FireBallStaff, ScrollofDamage,\ - ScrollofWeakening, Monocle -from ..entities.monsters import Rabbit, GiantSeaEagle +from ..entities.friendly import Chest, Merchant, Sunflower +from ..entities.items import Bomb, Bow, Chestplate, Explosion, FireBallStaff, \ + Heart, Helmet, Monocle, RingCritical, ScrollofDamage, ScrollofWeakening, \ + Shield, Sword +from ..entities.monsters import GiantSeaEagle, Rabbit from ..entities.player import Player -from ..enums import DisplayActions, KeyValues, GameMode +from ..enums import DisplayActions, GameMode, KeyValues from ..game import Game from ..interfaces import Map from ..menus import MainMenuValues diff --git a/squirrelbattle/tests/interfaces_test.py b/squirrelbattle/tests/interfaces_test.py index 77ae53a..c5a8b74 100644 --- a/squirrelbattle/tests/interfaces_test.py +++ b/squirrelbattle/tests/interfaces_test.py @@ -3,9 +3,9 @@ import unittest -from squirrelbattle.display.texturepack import TexturePack -from squirrelbattle.interfaces import Map, Tile, Slope -from squirrelbattle.resources import ResourceManager +from ..display.texturepack import TexturePack +from ..interfaces import Map, Slope, Tile +from ..resources import ResourceManager class TestInterfaces(unittest.TestCase): diff --git a/squirrelbattle/translations.py b/squirrelbattle/translations.py index b4cddfc..19fffa9 100644 --- a/squirrelbattle/translations.py +++ b/squirrelbattle/translations.py @@ -3,9 +3,9 @@ import gettext as gt import os +from pathlib import Path import re import subprocess -from pathlib import Path from typing import Any, List diff --git a/tox.ini b/tox.ini index 1d2f15a..865dd93 100644 --- a/tox.ini +++ b/tox.ini @@ -19,6 +19,7 @@ deps = flake8 flake8-annotations flake8-colors + flake8-import-order flake8-typing-imports pep8-naming pyflakes From 6d786c7358b25b91981ff2572f1ace4df5febf82 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 11:30:04 +0100 Subject: [PATCH 19/52] Documentation on unit tests --- docs/tests.rst | 48 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/docs/tests.rst b/docs/tests.rst index 3bdd0d2..039f812 100644 --- a/docs/tests.rst +++ b/docs/tests.rst @@ -1,12 +1,50 @@ Exécution des tests =================== -.. note:: - La documentation va être revue ici. - -Les tests sont gérés par ``pytest`` dans le module ``squirrelbattle.tests``. - ``tox`` est un outil permettant de configurer l'exécution des tests. Ainsi, après installation de tox dans votre environnement virtuel via ``pip install tox``, il vous suffit d'exécuter ``tox -e py3`` pour lancer les tests et ``tox -e linters`` pour vérifier la syntaxe du code. + +Tests unitaires +--------------- + +Les tests sont gérés par ``pytest`` dans le module ``squirrelbattle.tests``. +Le module ``pytest-cov`` permet de mesurer la couverture du code. +Pour lancer les tests, il suffit de lancer ``tox -e py3`` ou de manière équivalente +``pytest --cov=squirrelbattle/ --cov-report=term-missing squirrelbattle/`` + +L'intégration continue lance les tests pour les versions de Python de 3.6 à 3.10, +sur une distribution Alpine Linux. + +Chaque partie du code est testée unitairement, pour obtenir une couverture +maximale et assurer un bon fonctionnement. En particulier, le jeu est lancé +en commençant sur une carte déterministe (non générée aléatoirement) chargée +depuis ``assets/example_map.txt``, sur laquelle sont placés des ennemis et objets +avec lesquels le joueur doit interagir. On vérifie qu'à chaque touche appuyée, +il se passe le bon comportement. Le comportement des différents menus est +également testé. + +L'environnement de test ne disposant pas a priori d'un terminal, le jeu est +conçu pour fonctionner sans support graphique, avec un terminal fictif où les +primitives de curses sont implémentées pour ne rien faire. On ne peut alors +pas s'assurer du bon fonctionnement de curses. + +De plus, une très fine partie du code est ignorée lors de la couverture, ce +qui correspond à la phase d'initialisation du terminal et à la boucle infinie +qui reçoit les touches de l'utilisateur, qu'il est alors impossible de tester +unitairement. + + +Analyseur syntaxique +-------------------- + +``flake8`` est utilisé en guise d'analyseur syntaxique. Il vérifie si le code +est bien formatté afin d'assurer une certaine lisibilité. En particulier, +il vérifie l'indentation, si chaque variable est bien utilisée, s'il n'y a pas +d'import inutile et s'ils sont dans l'ordre lexicographique, si chaque ligne +fait au plus 80 caractères et si la signature de chaque fonction est bien +spécifiée. + +Pour lancer l'analyse, ``tox -e linters`` suffit. L'intégration continue +effectue cette analyse à chaque commit. From dd37c2f62f29c0cf320dc5a5cba6cb4ec5c1206c Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 11:54:49 +0100 Subject: [PATCH 20/52] Renamed the title's ascii art file. --- squirrelbattle/display/menudisplay.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index 64d69b7..024ba1c 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -105,7 +105,8 @@ class MainMenuDisplay(Display): super().__init__(*args) self.menu = menu - with open(ResourceManager.get_asset_path("ascii_art.txt"), "r") as file: + with open(ResourceManager.get_asset_path("ascii_art-title.txt"), "r")\ + as file: self.title = file.read().split("\n") self.pad = self.newpad(max(self.rows, len(self.title) + 30), From 92a3081b2e6b901bf1299f897ce1e6e4b55904be Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 11:58:32 +0100 Subject: [PATCH 21/52] Display "Edit on Gitlab" button --- docs/conf.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 0d65dd0..ffab7ea 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -61,3 +61,11 @@ html_theme = 'sphinx_rtd_theme' # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] + +html_context = { + 'gitlab_user': 'ynerant', + 'gitlab_repo': 'squirrel-battle', + 'gitlab_host': 'gitlab.crans.org', + 'gitlab_version': 'master', + 'display_gitlab': True, +} From 0ef5875a72fd32bf65cc121eeeba7ed521857880 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 12:07:53 +0100 Subject: [PATCH 22/52] Documentation on ladders --- docs/map.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/map.rst b/docs/map.rst index e8b90cc..6d8f34a 100644 --- a/docs/map.rst +++ b/docs/map.rst @@ -44,3 +44,17 @@ Mur Les murs délimitent les salles du donjon. Personne ne peut les traverser. Ils sont représentés par un dièse ``#`` dans le `pack de textures`_ ASCII et par une brique carrée ``🧱`` dans le `pack de textures`_ écureuil. + + +Échelles +~~~~~~~~ + +Les échelles sont les débuts et fin de niveau. Elles permettent de changer +d'étage en appuyant sur une touche. Elles sont représentées par un ``H`` dans +le `pack de textures`_ ASCII et par un émoji échelle ``🪜`` dans le +`pack de textures`_ écureuil. + +Lorsqu'on est sur l'échelle du début de niveau, appuyer sur ``<`` permet de +monter d'un étage (revenir au niveau précédent). Lorsqu'on est sur l'échelle +de fin de niveau, on génère une nouvelle carte si ce n'est pas déjà fait avec +des monstres plus forts, et on place le joueur sur cette nouvelle carte. From 5eb769930154263d9060d98bef2b4553d882c52a Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 12:35:50 +0100 Subject: [PATCH 23/52] Rearranged the display class files, related to issue #56. --- .../{ascii_art.txt => ascii_art-title.txt} | 0 squirrelbattle/display/creditsdisplay.py | 97 ---------------- squirrelbattle/display/display.py | 26 +++++ squirrelbattle/display/display_manager.py | 13 +-- .../{statsdisplay.py => gamedisplay.py} | 108 +++++++++++++++++- squirrelbattle/display/logsdisplay.py | 31 ----- squirrelbattle/display/mapdisplay.py | 87 -------------- squirrelbattle/display/menudisplay.py | 88 ++++++++++++++ squirrelbattle/display/messagedisplay.py | 32 ------ 9 files changed, 226 insertions(+), 256 deletions(-) rename squirrelbattle/assets/{ascii_art.txt => ascii_art-title.txt} (100%) delete mode 100644 squirrelbattle/display/creditsdisplay.py rename squirrelbattle/display/{statsdisplay.py => gamedisplay.py} (55%) delete mode 100644 squirrelbattle/display/logsdisplay.py delete mode 100644 squirrelbattle/display/mapdisplay.py delete mode 100644 squirrelbattle/display/messagedisplay.py diff --git a/squirrelbattle/assets/ascii_art.txt b/squirrelbattle/assets/ascii_art-title.txt similarity index 100% rename from squirrelbattle/assets/ascii_art.txt rename to squirrelbattle/assets/ascii_art-title.txt diff --git a/squirrelbattle/display/creditsdisplay.py b/squirrelbattle/display/creditsdisplay.py deleted file mode 100644 index e005c5b..0000000 --- a/squirrelbattle/display/creditsdisplay.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (C) 2020-2021 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 update(self, game: Game) -> None: - return - - 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) -> None: - 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 - 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, bold=bold) - - def handle_click(self, y: int, x: int, attr: int, game: Game) -> None: - if self.pad.inch(y - 1, x - 1) != ord(" "): - self.ascii_art_displayed = True diff --git a/squirrelbattle/display/display.py b/squirrelbattle/display/display.py index 9b6e97c..3bf06c5 100644 --- a/squirrelbattle/display/display.py +++ b/squirrelbattle/display/display.py @@ -290,3 +290,29 @@ class Box(Display): self.refresh_pad(self.pad, 0, 0, self.y, self.x, self.y + self.height - 1, self.x + self.width - 1) + + +class MessageDisplay(Display): + """ + A class to handle the display of popup messages. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.box = Box(fg_border_color=curses.COLOR_RED, *args, **kwargs) + self.message = "" + self.pad = self.newpad(1, 1) + + def update(self, game: Game) -> None: + self.message = game.message + + def display(self) -> None: + self.box.refresh(self.y - 1, self.x - 2, + self.height + 2, self.width + 4) + self.box.display() + self.pad.erase() + self.addstr(self.pad, 0, 0, self.message, bold=True) + self.refresh_pad(self.pad, 0, 0, self.y, self.x, + self.height + self.y - 1, + self.width + self.x - 1) diff --git a/squirrelbattle/display/display_manager.py b/squirrelbattle/display/display_manager.py index 81cba4f..897617a 100644 --- a/squirrelbattle/display/display_manager.py +++ b/squirrelbattle/display/display_manager.py @@ -4,14 +4,11 @@ import curses from typing import Any, List -from .creditsdisplay import CreditsDisplay -from .display import Display, HorizontalSplit, VerticalSplit -from .logsdisplay import LogsDisplay -from .mapdisplay import MapDisplay -from .menudisplay import ChestInventoryDisplay, MainMenuDisplay, \ - PlayerInventoryDisplay, SettingsMenuDisplay, StoreInventoryDisplay -from .messagedisplay import MessageDisplay -from .statsdisplay import StatsDisplay +from .display import Display, HorizontalSplit, MessageDisplay, VerticalSplit +from .gamedisplay import LogsDisplay, MapDisplay, StatsDisplay +from .menudisplay import ChestInventoryDisplay, CreditsDisplay, \ + MainMenuDisplay, PlayerInventoryDisplay, \ + SettingsMenuDisplay, StoreInventoryDisplay from .texturepack import TexturePack from ..enums import DisplayActions from ..game import Game, GameMode diff --git a/squirrelbattle/display/statsdisplay.py b/squirrelbattle/display/gamedisplay.py similarity index 55% rename from squirrelbattle/display/statsdisplay.py rename to squirrelbattle/display/gamedisplay.py index b80fa7b..5efaf56 100644 --- a/squirrelbattle/display/statsdisplay.py +++ b/squirrelbattle/display/gamedisplay.py @@ -7,10 +7,116 @@ from .display import Display from ..entities.items import Monocle from ..entities.player import Player from ..game import Game -from ..interfaces import FightingEntity +from ..interfaces import FightingEntity, Logs, Map from ..translations import gettext as _ +class LogsDisplay(Display): + """ + A class to handle the display of the logs. + """ + + logs: Logs + + def __init__(self, *args) -> None: + super().__init__(*args) + self.pad = self.newpad(self.rows, self.cols) + + def update(self, game: Game) -> None: + self.logs = game.logs + + def display(self) -> None: + messages = self.logs.messages[-self.height:] + messages = messages[::-1] + self.pad.erase() + for i in range(min(self.height, len(messages))): + self.addstr(self.pad, self.height - i - 1, self.x, + messages[i][:self.width]) + self.refresh_pad(self.pad, 0, 0, self.y, self.x, + self.y + self.height - 1, self.x + self.width - 1) + + +class MapDisplay(Display): + """ + A class to handle the display of the map. + """ + + map: Map + + def __init__(self, *args): + super().__init__(*args) + + def update(self, game: Game) -> None: + self.map = game.map + self.pad = self.newpad(self.map.height, + self.pack.tile_width * self.map.width + 1) + + def update_pad(self) -> None: + for j in range(len(self.map.tiles)): + for i in range(len(self.map.tiles[j])): + if not self.map.seen_tiles[j][i]: + continue + fg, bg = self.map.tiles[j][i].visible_color(self.pack) if \ + self.map.visibility[j][i] else \ + self.map.tiles[j][i].hidden_color(self.pack) + self.addstr(self.pad, j, self.pack.tile_width * i, + self.map.tiles[j][i].char(self.pack), fg, bg) + for e in self.map.entities: + if self.map.visibility[e.y][e.x]: + self.addstr(self.pad, e.y, self.pack.tile_width * e.x, + self.pack[e.name.upper()], + self.pack.entity_fg_color, + self.pack.entity_bg_color) + + # Display Path map for debug purposes + # from squirrelbattle.entities.player import Player + # players = [ p for p in self.map.entities if isinstance(p,Player) ] + # player = players[0] if len(players) > 0 else None + # if player: + # for x in range(self.map.width): + # for y in range(self.map.height): + # if (y,x) in player.paths: + # deltay, deltax = (y - player.paths[(y, x)][0], + # x - player.paths[(y, x)][1]) + # if (deltay, deltax) == (-1, 0): + # character = '↓' + # elif (deltay, deltax) == (1, 0): + # character = '↑' + # elif (deltay, deltax) == (0, -1): + # character = '→' + # else: + # character = '←' + # self.addstr(self.pad, y, self.pack.tile_width * x, + # character, self.pack.tile_fg_color, + # self.pack.tile_bg_color) + + def display(self) -> None: + y, x = self.map.currenty, self.pack.tile_width * self.map.currentx + deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1 + pminrow, pmincol = y - deltay, x - deltax + sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) + deltay, deltax = self.height - deltay, self.width - deltax + smaxrow = self.map.height - (y + deltay) + self.height - 1 + smaxrow = min(smaxrow, self.height - 1) + smaxcol = self.pack.tile_width * self.map.width - \ + (x + deltax) + self.width - 1 + + # Wrap perfectly the map according to the width of the tiles + pmincol = self.pack.tile_width * (pmincol // self.pack.tile_width) + smincol = self.pack.tile_width * (smincol // self.pack.tile_width) + smaxcol = self.pack.tile_width \ + * (smaxcol // self.pack.tile_width + 1) - 1 + + smaxcol = min(smaxcol, self.width - 1) + pminrow = max(0, min(self.map.height, pminrow)) + pmincol = max(0, min(self.pack.tile_width * self.map.width, pmincol)) + + self.pad.erase() + self.update_pad() + self.refresh_pad(self.pad, pminrow, pmincol, sminrow, smincol, smaxrow, + smaxcol) + + class StatsDisplay(Display): """ A class to handle the display of the stats of the player. diff --git a/squirrelbattle/display/logsdisplay.py b/squirrelbattle/display/logsdisplay.py deleted file mode 100644 index 1c323af..0000000 --- a/squirrelbattle/display/logsdisplay.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse -# SPDX-License-Identifier: GPL-3.0-or-later - -from squirrelbattle.display.display import Display -from squirrelbattle.game import Game -from squirrelbattle.interfaces import Logs - - -class LogsDisplay(Display): - """ - A class to handle the display of the logs. - """ - - logs: Logs - - def __init__(self, *args) -> None: - super().__init__(*args) - self.pad = self.newpad(self.rows, self.cols) - - def update(self, game: Game) -> None: - self.logs = game.logs - - def display(self) -> None: - messages = self.logs.messages[-self.height:] - messages = messages[::-1] - self.pad.erase() - for i in range(min(self.height, len(messages))): - self.addstr(self.pad, self.height - i - 1, self.x, - messages[i][:self.width]) - self.refresh_pad(self.pad, 0, 0, self.y, self.x, - self.y + self.height - 1, self.x + self.width - 1) diff --git a/squirrelbattle/display/mapdisplay.py b/squirrelbattle/display/mapdisplay.py deleted file mode 100644 index 80e6ada..0000000 --- a/squirrelbattle/display/mapdisplay.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse -# SPDX-License-Identifier: GPL-3.0-or-later - -from .display import Display -from ..game import Game -from ..interfaces import Map - - -class MapDisplay(Display): - """ - A class to handle the display of the map. - """ - - map: Map - - def __init__(self, *args): - super().__init__(*args) - - def update(self, game: Game) -> None: - self.map = game.map - self.pad = self.newpad(self.map.height, - self.pack.tile_width * self.map.width + 1) - - def update_pad(self) -> None: - for j in range(len(self.map.tiles)): - for i in range(len(self.map.tiles[j])): - if not self.map.seen_tiles[j][i]: - continue - fg, bg = self.map.tiles[j][i].visible_color(self.pack) if \ - self.map.visibility[j][i] else \ - self.map.tiles[j][i].hidden_color(self.pack) - self.addstr(self.pad, j, self.pack.tile_width * i, - self.map.tiles[j][i].char(self.pack), fg, bg) - for e in self.map.entities: - if self.map.visibility[e.y][e.x]: - self.addstr(self.pad, e.y, self.pack.tile_width * e.x, - self.pack[e.name.upper()], - self.pack.entity_fg_color, - self.pack.entity_bg_color) - - # Display Path map for debug purposes - # from squirrelbattle.entities.player import Player - # players = [ p for p in self.map.entities if isinstance(p,Player) ] - # player = players[0] if len(players) > 0 else None - # if player: - # for x in range(self.map.width): - # for y in range(self.map.height): - # if (y,x) in player.paths: - # deltay, deltax = (y - player.paths[(y, x)][0], - # x - player.paths[(y, x)][1]) - # if (deltay, deltax) == (-1, 0): - # character = '↓' - # elif (deltay, deltax) == (1, 0): - # character = '↑' - # elif (deltay, deltax) == (0, -1): - # character = '→' - # else: - # character = '←' - # self.addstr(self.pad, y, self.pack.tile_width * x, - # character, self.pack.tile_fg_color, - # self.pack.tile_bg_color) - - def display(self) -> None: - y, x = self.map.currenty, self.pack.tile_width * self.map.currentx - deltay, deltax = (self.height // 2) + 1, (self.width // 2) + 1 - pminrow, pmincol = y - deltay, x - deltax - sminrow, smincol = max(-pminrow, 0), max(-pmincol, 0) - deltay, deltax = self.height - deltay, self.width - deltax - smaxrow = self.map.height - (y + deltay) + self.height - 1 - smaxrow = min(smaxrow, self.height - 1) - smaxcol = self.pack.tile_width * self.map.width - \ - (x + deltax) + self.width - 1 - - # Wrap perfectly the map according to the width of the tiles - pmincol = self.pack.tile_width * (pmincol // self.pack.tile_width) - smincol = self.pack.tile_width * (smincol // self.pack.tile_width) - smaxcol = self.pack.tile_width \ - * (smaxcol // self.pack.tile_width + 1) - 1 - - smaxcol = min(smaxcol, self.width - 1) - pminrow = max(0, min(self.map.height, pminrow)) - pmincol = max(0, min(self.pack.tile_width * self.map.width, pmincol)) - - self.pad.erase() - self.update_pad() - self.refresh_pad(self.pad, pminrow, pmincol, sminrow, smincol, smaxrow, - smaxcol) diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index a502b63..54acaed 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -282,3 +282,91 @@ class ChestInventoryDisplay(MenuDisplay): self.menu.position = max(0, min(len(self.menu.values) - 1, y - 2)) game.is_in_chest_menu = True game.handle_key_pressed(KeyValues.ENTER) + + +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 update(self, game: Game) -> None: + return + + 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) -> None: + 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 + 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, bold=bold) + + def handle_click(self, y: int, x: int, attr: int, game: Game) -> None: + if self.pad.inch(y - 1, x - 1) != ord(" "): + self.ascii_art_displayed = True diff --git a/squirrelbattle/display/messagedisplay.py b/squirrelbattle/display/messagedisplay.py deleted file mode 100644 index 2b1ec30..0000000 --- a/squirrelbattle/display/messagedisplay.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse -# SPDX-License-Identifier: GPL-3.0-or-later -import curses - -from squirrelbattle.display.display import Box, Display -from squirrelbattle.game import Game - - -class MessageDisplay(Display): - """ - A class to handle the display of popup messages. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.box = Box(fg_border_color=curses.COLOR_RED, *args, **kwargs) - self.message = "" - self.pad = self.newpad(1, 1) - - def update(self, game: Game) -> None: - self.message = game.message - - def display(self) -> None: - self.box.refresh(self.y - 1, self.x - 2, - self.height + 2, self.width + 4) - self.box.display() - self.pad.erase() - self.addstr(self.pad, 0, 0, self.message, bold=True) - self.refresh_pad(self.pad, 0, 0, self.y, self.x, - self.height + self.y - 1, - self.width + self.x - 1) From 951623089373ede620c26aaae20adcbbaf311afc Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 15:38:39 +0100 Subject: [PATCH 24/52] We now have a useful Readme with a getting started manual. --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6898db..36482de 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,60 @@ # Squirrel Battle -Attention aux couteaux des écureuils ! +Squirrel Battle is an infinite rogue-like game with randomly generated levels, in which the player controls a squirrel in its quest down in a dungeon, using diverse items to defeat monsters, and trying not to die. + +##Installation + +####Via PyPI : +``` pip install --user squirrel-battle +``` to install + +``` pip install --user --upgrade squirrel-battle +``` to upgrade + +####Via ArchLinux package : +Download one of these two packages on the AUR : + +* python-squirrel-battle +* python-squirrel-battle-git + +####Via Debian package : +Available on our git repository, has a dependency on fonts-noto-color-emoji (to be found in the official Debian repositories). + +Run ``` + dpkg -i python3-squirrelbattle_3.14.1_all.deb +``` after downloading + +In all cases, execute via command line : ```squirrel-battle``` + +##For first-time players + +The game is played in a terminal only, preferably one that supports color, markdown and emojis, but it can be played with only grey levels and relatively classic unicode characters. + +Upon starting, the game will display the main menu. To navigate in menus, use zqsd or the keyboard arrows. To validate one of the options, use the Enter key. Mouse click is also supported in most menus, **but not in game**. + +The game in itself can have two types of display : using ascii and simple unicode characters, or using emojis. To activate emoji mode, go to the settings menu and select the squirrel texture pack. Emojis will not work if the terminal does not support them, so do tests before to ensure the terminal can display them. + +The game is translated (almost entirely) in English, French, German and Spanish. To change the language, go to the settings menu. + +Controls in-game are pretty basic : use zqsd or the keyboard arrows to move. To hit an ennemy, simply go in its direction if it is in an adjacent tile. + +There are several special control keys. They can be changed in the settings menu. + +* To close a store menu or go back to the main menu, use Space +* To open/close the inventory, use i +* To use an object in the inventory, use u +* To equip an object in the inventory, use e +* To use a long range weapon after it has been equipped, use l and then select the direction to shoot in +* To drop an object from the inventory, use r (to pick up an object, simply go on its tile, its automatic) +* To talk to certains entities (or open a chest), use t and then select the direction of the entity +* To wait a turn (rather than moving), use w +* To use a ladder, use < + +And that is all you need to get started! You can now start your adventure and don't worry, floors are randomly generated, so it won't always be the same boring level. ## Documentation -La documentation du projet est présente sur [squirrel-battle.readthedocs.io](https://squirrel-battle.readthedocs.io). +The documentation for the project cen be found at [squirrel-battle.readthedocs.io](https://squirrel-battle.readthedocs.io). It is unfortunately only written in French. + +Anyone interested in understanding how the code works can find a few explanations in the documentation. From 97ecd13c778abfb610013ef037f98b86a9fe8639 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 15:45:26 +0100 Subject: [PATCH 25/52] Readme got even better ! --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 36482de..4c1356c 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ The game is translated (almost entirely) in English, French, German and Spanish. Controls in-game are pretty basic : use zqsd or the keyboard arrows to move. To hit an ennemy, simply go in its direction if it is in an adjacent tile. -There are several special control keys. They can be changed in the settings menu. +There are several special control keys, they can be changed in the settings menu : * To close a store menu or go back to the main menu, use Space * To open/close the inventory, use i @@ -59,6 +59,8 @@ There are several special control keys. They can be changed in the settings menu * To wait a turn (rather than moving), use w * To use a ladder, use < +The dungeon consists in empty tiles (you can not go there), walls (which you can not cross) and floor ( :) ). Entities that move are usually monsters, but if you see a trumpet (or a '/'), do not kill it ! It is a familiar that will help you defeat monsters. Entities that do not move are either entities to which you can talk, like merchants and ... chests for some reason, or objects. Differentiating the two is not difficult, trying to go on the same tile as a living entity (or a chest) is impossible. Objects have pretty clear names, so it should not be too difficult determining what they do (if you still don't know, you can either read the docs, or test for yourself (beware of surprises though)) + And that is all you need to get started! You can now start your adventure and don't worry, floors are randomly generated, so it won't always be the same boring level. ## Documentation From 841c7b9f90dc19b75870d8b3a1b0016a99d3643a Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 16:31:16 +0100 Subject: [PATCH 26/52] Chest can be destroyed by bombs. --- squirrelbattle/entities/friendly.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 36f9db3..ee4f7cd 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -3,7 +3,7 @@ from random import choice, shuffle -from .items import Item +from .items import Item, Bomb from .monsters import Monster from .player import Player from ..interfaces import Entity, FightingEntity, FriendlyEntity, \ @@ -68,6 +68,9 @@ class Chest(InventoryHolder, FriendlyEntity): """ A chest is not living, it can not take damage """ + if isinstance(attacker, Bomb) : + self.die() + return _("The chest exploded") return _("It's not really effective") @property @@ -82,7 +85,7 @@ class Sunflower(FriendlyEntity): """ A friendly sunflower. """ - def __init__(self, maxhealth: int = 15, + def __init__(self, maxhealth: int = 20, *args, **kwargs) -> None: super().__init__(name="sunflower", maxhealth=maxhealth, *args, **kwargs) @@ -162,6 +165,6 @@ class Trumpet(Familiar): A class of familiars. """ def __init__(self, name: str = "trumpet", strength: int = 3, - maxhealth: int = 20, *args, **kwargs) -> None: + maxhealth: int = 30, *args, **kwargs) -> None: super().__init__(name=name, strength=strength, maxhealth=maxhealth, *args, **kwargs) From dfb591d410eff4c0e126aef58c700440c6619109 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 16:31:46 +0100 Subject: [PATCH 27/52] The player's stats now get better when levelling up. The strength level and frequency of appearance of entities have been changed to offer bettter game experience. --- squirrelbattle/entities/monsters.py | 6 +++--- squirrelbattle/entities/player.py | 13 ++++++++++++- squirrelbattle/interfaces.py | 9 +++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/squirrelbattle/entities/monsters.py b/squirrelbattle/entities/monsters.py index c654428..8f3c2b5 100644 --- a/squirrelbattle/entities/monsters.py +++ b/squirrelbattle/entities/monsters.py @@ -76,8 +76,8 @@ class Tiger(Monster): """ A tiger monster. """ - def __init__(self, name: str = "tiger", strength: int = 2, - maxhealth: int = 20, *args, **kwargs) -> None: + def __init__(self, name: str = "tiger", strength: int = 5, + maxhealth: int = 30, *args, **kwargs) -> None: super().__init__(name=name, strength=strength, maxhealth=maxhealth, *args, **kwargs) @@ -97,7 +97,7 @@ class Rabbit(Monster): A rabbit monster. """ def __init__(self, name: str = "rabbit", strength: int = 1, - maxhealth: int = 15, critical: int = 30, + maxhealth: int = 20, critical: int = 30, *args, **kwargs) -> None: super().__init__(name=name, strength=strength, maxhealth=maxhealth, critical=critical, diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 8257f85..5b788b2 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -3,6 +3,7 @@ from random import randint from typing import Dict, Optional, Tuple +from math import log from .items import Item from ..interfaces import FightingEntity, InventoryHolder @@ -69,9 +70,19 @@ class Player(InventoryHolder, FightingEntity): self.level += 1 self.current_xp -= self.max_xp self.max_xp = self.level * 10 + self.maxhealth += int(2*log(self.level)/log(2)) self.health = self.maxhealth self.strength = self.strength + 1 - # TODO Remove it, that's only fun + if self.level % 3 == 0 : + self.dexterity += 1 + self.constitution += 1 + if self.level % 4 == 0 : + self.intelligence += 1 + if self.level % 6 == 0 : + self.charisma += 1 + if self.level % 10 == 0 and self.critical < 95: + self.critical += (100-self.charisma)//30 + # TODO Remove it, that's only for fun self.map.spawn_random_entities(randint(3 * self.level, 10 * self.level)) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 2280909..60a7c82 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -628,8 +628,9 @@ class Entity: Rabbit, TeddyBear, GiantSeaEagle from squirrelbattle.entities.friendly import Merchant, Sunflower, \ Trumpet, Chest - return [BodySnatchPotion, Bomb, Heart, Hedgehog, Rabbit, TeddyBear, - Sunflower, Tiger, Merchant, GiantSeaEagle, Trumpet, Chest] + return [BodySnatchPotion, Bomb, Chest, GiantSeaEagle, Heart, + Hedgehog, Merchant, Rabbit, Sunflower, TeddyBear, Tiger, + Trumpet] @staticmethod def get_weights() -> list: @@ -637,7 +638,7 @@ class Entity: Returns a weigth list associated to the above function, to be used to spawn random entities with a certain probability. """ - return [3, 5, 6, 5, 5, 5, 5, 4, 3, 1, 2, 4] + return [30, 80, 50, 1, 100, 100, 60, 70, 70, 20, 40, 40] @staticmethod def get_all_entity_classes_in_a_dict() -> dict: @@ -765,7 +766,7 @@ class FightingEntity(Entity): The entity takes damage from the attacker based on their respective stats. """ - damage = max(0, amount - self.constitution) + damage = max(1, amount - self.constitution) self.health -= damage if self.health <= 0: self.die() From 3d48c43886223503587407e0b857d85816e113f2 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 17:10:00 +0100 Subject: [PATCH 28/52] Player can now dance! Closes #69. --- squirrelbattle/entities/items.py | 2 +- squirrelbattle/entities/player.py | 27 +++++++++++++++++++++++++++ squirrelbattle/enums.py | 3 +++ squirrelbattle/game.py | 4 ++++ squirrelbattle/interfaces.py | 6 ++++++ squirrelbattle/settings.py | 1 + 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 94a9e36..ac502ea 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -498,7 +498,7 @@ class ScrollofDamage(Item): class ScrollofWeakening(Item): """ - A scroll that, when used, reduces the damage of the ennemies for 3 turn. + A scroll that, when used, reduces the damage of the ennemies for 3 turns. """ def __init__(self, name: str = "scroll_of_weakening", price: int = 13, *args, **kwargs): diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 5b788b2..b3cb986 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -7,6 +7,7 @@ from math import log from .items import Item from ..interfaces import FightingEntity, InventoryHolder +from ..translations import gettext as _, Translator class Player(InventoryHolder, FightingEntity): @@ -62,6 +63,32 @@ class Player(InventoryHolder, FightingEntity): self.recalculate_paths() self.map.compute_visibility(self.y, self.x, self.vision) + def dance(self) -> None: + """ + Dancing has a certain probability or making ennemies unable + to fight for 2 turns. That probability depends on the player's + charisma. + """ + diceroll = randint(1, 10) + found = False + if diceroll <= self.charisma: + for entity in self.map.entities: + if entity.is_fighting_entity() and not entity == self \ + and entity.distance(self)<=3: + found = True + entity.confused = 1 + entity.effects.append(["confused", 1, 3]) + if found: + self.map.logs.add_message(_( + "It worked! Nearby ennemies will be confused for 3 turns.")) + else: + self.map.logs.add_message(_( + "It worked, but there is no one nearby...")) + else: + self.map.logs.add_message( + _("The dance was not effective...")) + + def level_up(self) -> None: """ Add as many levels as possible to the player. diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index b6b4bcd..42bd643 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -50,6 +50,7 @@ class KeyValues(Enum): WAIT = auto() LADDER = auto() LAUNCH = auto() + DANCE = auto() @staticmethod def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]: @@ -88,4 +89,6 @@ class KeyValues(Enum): return KeyValues.LADDER elif key == settings.KEY_LAUNCH: return KeyValues.LAUNCH + elif key == settings.KEY_DANCE: + return KeyValues.DANCE return None diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 56ab6ed..73d45eb 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -179,6 +179,9 @@ class Game: self.map.tick(self.player) elif key == KeyValues.LADDER: self.handle_ladder() + elif key == KeyValues.DANCE: + self.player.dance() + self.map.tick(self.player) def handle_ladder(self) -> None: """ @@ -291,6 +294,7 @@ class Game: if self.player.equipped_main: self.player.equipped_main.throw(direction) + def handle_key_pressed_inventory(self, key: KeyValues) -> None: """ In the inventory menu, we can interact with items or close the menu. diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 60a7c82..3832cef 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -707,6 +707,7 @@ class FightingEntity(Entity): constitution: int level: int critical: int + confused: int # Seulement 0 ou 1 def __init__(self, maxhealth: int = 0, health: Optional[int] = None, strength: int = 0, intelligence: int = 0, charisma: int = 0, @@ -723,6 +724,7 @@ class FightingEntity(Entity): self.level = level self.critical = critical self.effects = [] # effects = temporary buff or weakening of the stats. + self.confused = 0 @property def dead(self) -> bool: @@ -750,6 +752,10 @@ class FightingEntity(Entity): The entity deals damage to the opponent based on their respective stats. """ + if self.confused: + return _("{name} is confused, it can not hit {opponent}.")\ + .format(name=_(self.translated_name.capitalize()), + opponent=_(opponent.translated_name)) diceroll = randint(1, 100) damage = max(0, self.strength) string = " " diff --git a/squirrelbattle/settings.py b/squirrelbattle/settings.py index 92a8b37..3fd27c5 100644 --- a/squirrelbattle/settings.py +++ b/squirrelbattle/settings.py @@ -36,6 +36,7 @@ class Settings: self.KEY_WAIT = ['w', 'Key used to wait'] self.KEY_LADDER = ['<', 'Key used to use ladders'] self.KEY_LAUNCH = ['l', 'Key used to use a bow'] + self.KEY_DANCE = ['y', 'Key used to dance'] self.TEXTURE_PACK = ['ascii', 'Texture pack'] self.LOCALE = [locale.getlocale()[0][:2], 'Language'] From afaa12d86bb7e9c612481944c782bd467375bf09 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 17:13:07 +0100 Subject: [PATCH 29/52] Updated doc and README conderning dancing --- README.md | 1 + docs/settings.rst | 3 +++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 4c1356c..ae7be80 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ There are several special control keys, they can be changed in the settings menu * To drop an object from the inventory, use r (to pick up an object, simply go on its tile, its automatic) * To talk to certains entities (or open a chest), use t and then select the direction of the entity * To wait a turn (rather than moving), use w +* To dance and confuse the ennemies, use y * To use a ladder, use < The dungeon consists in empty tiles (you can not go there), walls (which you can not cross) and floor ( :) ). Entities that move are usually monsters, but if you see a trumpet (or a '/'), do not kill it ! It is a familiar that will help you defeat monsters. Entities that do not move are either entities to which you can talk, like merchants and ... chests for some reason, or objects. Differentiating the two is not difficult, trying to go on the same tile as a living entity (or a chest) is impossible. Objects have pretty clear names, so it should not be too difficult determining what they do (if you still don't know, you can either read the docs, or test for yourself (beware of surprises though)) diff --git a/docs/settings.rst b/docs/settings.rst index 60fa5c1..de17fca 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -27,6 +27,9 @@ Les touches utilisées de base sont : * **Lacher un objet** : r * **Parler** : t * **Attendre** : w +* **Utiliser une arme à distance** : l +* **Dancer** : y +* **Utiliser une échelle** : < Autres ------ From 568b8f3eba9ab4620bbcf9eb6cc363e2df635a2f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 17:33:06 +0100 Subject: [PATCH 30/52] Documentation on displays --- docs/display/index.rst | 55 ++++++++++++++++++++++++++++++++++++------ docs/display/logs.rst | 5 +++- docs/display/map.rst | 11 +++++++-- docs/display/menu.rst | 17 +++++++------ docs/display/stats.rst | 9 +++++-- 5 files changed, 78 insertions(+), 19 deletions(-) diff --git a/docs/display/index.rst b/docs/display/index.rst index 376466c..a32c052 100644 --- a/docs/display/index.rst +++ b/docs/display/index.rst @@ -3,13 +3,6 @@ Gestion de l'affichage .. _curses: https://docs.python.org/3/howto/curses.html -L'intégralité de l'affichage du jeu est géré grâce à la bibliothèque native de -Python curses_. - - -.. warning:: - Plus de documentation à venir. - .. toctree:: :maxdepth: 3 @@ -19,3 +12,51 @@ Python curses_. map stats logs + +L'intégralité de l'affichage du jeu est géré grâce à la bibliothèque native de Python curses_. + +Initialisation du terminal +-------------------------- + +Au lancement du jeu, le terminal est initialisé, les caractères spéciaux sont traduits en abstraction curses, les +caractères tapés par l'utilisateur ne sont plus affichés sur le terminal, on envoie les touches tapées à curses en +permanence sans avoir à taper sur Entrée, le curseur est rendu invisible, on active le support des couleurs et enfin +on déclare vouloir attraper tous les clics de souris. Tout cela est fait dans un contexte Python, qui permet +d'effectuer les opérations inverses lors de la fermeture du programme, même en cas de crash, afin de retrouver un +terminal utilisable. + + +Pads +---- + +Chaque morceau d'affichage est géré dans un *pad*. Un pad est un objet défini par curses, représentant une sous-fenêtre, +qui a l'avantage d'être un peu plus flexible qu'une simple fenêtre. Un pad peut en effet dépasser de l'écran, et on +peut choisir où placer le coin avant gauche et quelle partie du pad il faut dessiner sur la fenêtre. + +Ce projet implémente une couche d'abstraction supplémentaire, afin d'avoir des objets plus utilisables. Chaque partie +d'affichage est réprésentée dans une classé étendant ``Display``. Chaque ``Display`` contient un (ou plusieurs) pads, +et propose une surcouche de certaines fonctions de curses. + +L'affichage de texte par exemple sur un pad est plus sécurisée que celle proposée par curses. Le comportement par défaut +est d'afficher un message à partir d'une position donnée avec des attributs (gras, couleur, ...) donnés sous +forme numérique. Cette implémentation permet de passer les attributs voulus sous forme de paramètres booléens, de +choisir la couleur de front/de fond sans définir de paire curses, mais surtout de tronquer le texte à la place +disponible, afin de ne pas avoir à se soucier d'avoir un message trop grand et éviter des crashs non désirés. + +Les ``Display`` sont gérés par un ``DisplayManager``. C'est lui qui décide, en fonction de l'état actuel du jeu, +d'afficher les bons ``Display`` aux bons endroits et de les redimensionner correctement en fonction de la taille du +terminal. C'est aussi lui qui propage l'information de modifier les attributs d'un ``Display``, si par exemple +l'inventaire du joueur a été mis à jour. + +Il s'occupe enfin de tout redimensionner si jamais le terminal a changé de taille, après une intervention +de l'utilisateur. + + +Interactions avec la souris +--------------------------- + +Le jeu attrape les clics de souris. C'est le ``DisplayManager``, connaissant l'état du jeu et ce qui est affiché à +quel endroit, qui va chercher sur quel ``Display`` on a cliqué. L'information est propagée au bon ``Display``, en +adaptant les coordonnées. + +Tout ``Display`` qui contient un menu procède de la même façon pour propager l'information au bon menu. diff --git a/docs/display/logs.rst b/docs/display/logs.rst index 9e925a3..b46147d 100644 --- a/docs/display/logs.rst +++ b/docs/display/logs.rst @@ -1,4 +1,7 @@ Affichage de l'historique ========================= -L'historique des actions est affiché en bas de l'écran. À chaque action d'une entité, comme frapper quelqu'un, ou lorsque le joueur parle à une entité, cela s'affiche dans l'historique. +L'historique des actions est affiché en bas de l'écran. À chaque action d'une entité, comme frapper quelqu'un, +ou lorsque le joueur parle à une entité, cela s'affiche dans l'historique. + +Il est affiché sur l'écran de jeu, en bas à gauche, occupant 1/5 de la hauteur et 4/5 de la largeur. diff --git a/docs/display/map.rst b/docs/display/map.rst index 749b32c..2b80be5 100644 --- a/docs/display/map.rst +++ b/docs/display/map.rst @@ -1,6 +1,13 @@ Affichage de la carte ===================== -La carte s'affiche dans la partie en haut à gauche de l'écran, sur la plus grande partie de l'écran. On affiche les tuiles une par une, selon le texture pack sélectionné. La map est actualisée à chaque action d'une entité. +La carte s'affiche dans la partie en haut à gauche de l'écran, sur la plus grande partie de l'écran. +On affiche les tuiles une par une. Selon le pack de textures utilisé, les tuiles prennent un ou deux caractères de large. +Selon la visibilité de la case en fonction de la position du joueur, la couleur de la case sera plus ou moins sombre, +voire masquée si le joueur n'a jamais vu la case. Les entités sont ensuite affichées, si elles sont dans le champ de +vision du joueur. -L'afffichage de la carte suit les déplacements du joueur. +La carte est actualisée lorsque cela est nécessaire, à chaque tick de jeu. + +L'afffichage de la carte suit les déplacements du joueur, dans le sens où la caméra est toujours centrée sur lui. +La carte prend 4/5 de l'affichage aussi bien en largeur qu'en hauteur. diff --git a/docs/display/menu.rst b/docs/display/menu.rst index 30c4e98..4282dc3 100644 --- a/docs/display/menu.rst +++ b/docs/display/menu.rst @@ -2,13 +2,16 @@ Affichage des menus =================== Les menus sont affichés dans une boîte. On peut naviguer dedans avec les flèches haut et bas, -et valider avec la touche entrée. +et valider avec la touche entrée. Il est également possible d'intéragir avec la souris. Il y a plusieurs menus dans le jeu : -* Le main menu, qui s'affiche au lancement du jeu. -* Le menu des paramètres : si on sélectionne un choix de touche et qu'on appuie sur entrée, on peut ensuite appuyer sur une touche pour remplacer la touche utilisée. -* Le menu des crédits : ce menu fonctionne avec la souris. En cliquant on affiche une image. -* Le menu d'inventaire : dans l'inventaire, on peut utiliser les touches pour utiliser un item ou l'équiper... -* Le menu de vente : on peut utiliser les touches gauche et droite pour switcher entre l'inventaire du joueur et celui du marchand. -* Menu des warnings : Pas vraiment un menu, mais affiche juste un message dans une petite boite pour prévenir le joueur que quelquechose ne va pas. +* **Le menu principal**, qui s'affiche au lancement du jeu. +* **Le menu des paramètres** : si on sélectionne un choix de touche et qu'on appuie sur entrée, + on peut ensuite appuyer sur une touche pour remplacer la touche utilisée. +* **Le menu des crédits** : ce menu fonctionne avec la souris. En cliquant on affiche une image. +* **Le menu d'inventaire** : dans l'inventaire, on peut utiliser les touches pour utiliser un item ou l'équiper... +* **Le menu de vente** : on peut utiliser les touches gauche et droite pour switcher entre l'inventaire du joueur + et celui du marchand. +* **Menu des warnings** : Pas vraiment un menu, mais affiche juste un message dans une petite boite pour prévenir + le joueur que quelque chose ne va pas. diff --git a/docs/display/stats.rst b/docs/display/stats.rst index e993c7f..9fc9075 100644 --- a/docs/display/stats.rst +++ b/docs/display/stats.rst @@ -4,7 +4,7 @@ Affichage des statistiques .. _Hazel: ../index.html Les statistiques du joueur sont affichées en haut à droite de l'écran -et séparées du reste de l'affichage par une barre verticale. +et séparées du reste de l'affichage par une barre verticale, occupant 1/5 de la place horizontale. Les informations affichées sont : @@ -23,4 +23,9 @@ Les informations affichées sont : * **Equipped armor** - le plastron porté par le joueur. * **Equipped helmet** - le casque porté par le joueur. * **Hazel** - le nombre d'Hazel_ que le joueur possède. -* **Vous êtes mort** - Éventuellement, si le joueur est mort. \ No newline at end of file +* **Vous êtes mort** - Éventuellement, si le joueur est mort. + +Si le joueur possède un `monocle <../entities/items.html#monocle>`_, alors les statistiques d'une entité proche sont +également affichées dessous. + +Des aides de jeu peuvent enfin être affichées en bas, telles que la touche sur laquelle il faut appuyer. From 93e51d9240940011f2d31d8971ace581b189215e Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 18:04:33 +0100 Subject: [PATCH 31/52] Testing + linting (yes there remains two linting errors, i don't know what to do. --- squirrelbattle/entities/friendly.py | 10 +++++--- squirrelbattle/entities/player.py | 23 ++++++++--------- squirrelbattle/game.py | 1 - squirrelbattle/interfaces.py | 6 ++--- squirrelbattle/tests/entities_test.py | 37 ++++++++++++++++++++++----- squirrelbattle/tests/game_test.py | 29 ++++++++++++++++++++- 6 files changed, 79 insertions(+), 27 deletions(-) diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index ee4f7cd..57506e9 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -3,7 +3,7 @@ from random import choice, shuffle -from .items import Item, Bomb +from .items import Bomb, Item from .monsters import Monster from .player import Player from ..interfaces import Entity, FightingEntity, FriendlyEntity, \ @@ -48,11 +48,14 @@ class Chest(InventoryHolder, FriendlyEntity): """ A class of chest inanimate entities which contain objects. """ + annihilated: bool + def __init__(self, name: str = "chest", inventory: list = None, hazel: int = 0, *args, **kwargs): super().__init__(name=name, *args, **kwargs) self.hazel = hazel self.inventory = self.translate_inventory(inventory or []) + self.annihilated = False if not self.inventory: for i in range(3): self.inventory.append(choice(Item.get_all_items())()) @@ -68,8 +71,9 @@ class Chest(InventoryHolder, FriendlyEntity): """ A chest is not living, it can not take damage """ - if isinstance(attacker, Bomb) : + if isinstance(attacker, Bomb): self.die() + self.annihilated = True return _("The chest exploded") return _("It's not really effective") @@ -78,7 +82,7 @@ class Chest(InventoryHolder, FriendlyEntity): """ Chest can not die """ - return False + return self.annihilated class Sunflower(FriendlyEntity): diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index b3cb986..7648639 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -1,13 +1,13 @@ # Copyright (C) 2020-2021 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later +from math import log from random import randint from typing import Dict, Optional, Tuple -from math import log from .items import Item from ..interfaces import FightingEntity, InventoryHolder -from ..translations import gettext as _, Translator +from ..translations import gettext as _ class Player(InventoryHolder, FightingEntity): @@ -74,10 +74,10 @@ class Player(InventoryHolder, FightingEntity): if diceroll <= self.charisma: for entity in self.map.entities: if entity.is_fighting_entity() and not entity == self \ - and entity.distance(self)<=3: - found = True - entity.confused = 1 - entity.effects.append(["confused", 1, 3]) + and entity.distance(self) <= 3: + found = True + entity.confused = 1 + entity.effects.append(["confused", 1, 3]) if found: self.map.logs.add_message(_( "It worked! Nearby ennemies will be confused for 3 turns.")) @@ -88,7 +88,6 @@ class Player(InventoryHolder, FightingEntity): self.map.logs.add_message( _("The dance was not effective...")) - def level_up(self) -> None: """ Add as many levels as possible to the player. @@ -97,18 +96,18 @@ class Player(InventoryHolder, FightingEntity): self.level += 1 self.current_xp -= self.max_xp self.max_xp = self.level * 10 - self.maxhealth += int(2*log(self.level)/log(2)) + self.maxhealth += int(2 * log(self.level) / log(2)) self.health = self.maxhealth self.strength = self.strength + 1 - if self.level % 3 == 0 : + if self.level % 3 == 0: self.dexterity += 1 self.constitution += 1 - if self.level % 4 == 0 : + if self.level % 4 == 0: self.intelligence += 1 - if self.level % 6 == 0 : + if self.level % 6 == 0: self.charisma += 1 if self.level % 10 == 0 and self.critical < 95: - self.critical += (100-self.charisma)//30 + self.critical += (100 - self.charisma) // 30 # TODO Remove it, that's only for fun self.map.spawn_random_entities(randint(3 * self.level, 10 * self.level)) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 73d45eb..bde825f 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -294,7 +294,6 @@ class Game: if self.player.equipped_main: self.player.equipped_main.throw(direction) - def handle_key_pressed_inventory(self, key: KeyValues) -> None: """ In the inventory menu, we can interact with items or close the menu. diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 3832cef..bf8ddbe 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -629,7 +629,7 @@ class Entity: from squirrelbattle.entities.friendly import Merchant, Sunflower, \ Trumpet, Chest return [BodySnatchPotion, Bomb, Chest, GiantSeaEagle, Heart, - Hedgehog, Merchant, Rabbit, Sunflower, TeddyBear, Tiger, + Hedgehog, Merchant, Rabbit, Sunflower, TeddyBear, Tiger, Trumpet] @staticmethod @@ -754,8 +754,8 @@ class FightingEntity(Entity): """ if self.confused: return _("{name} is confused, it can not hit {opponent}.")\ - .format(name=_(self.translated_name.capitalize()), - opponent=_(opponent.translated_name)) + .format(name=_(self.translated_name.capitalize()), + opponent=_(opponent.translated_name)) diceroll = randint(1, 100) damage = max(0, self.strength) string = " " diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index db32877..a0e2548 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -4,7 +4,7 @@ import random import unittest -from ..entities.friendly import Trumpet +from ..entities.friendly import Chest, Trumpet from ..entities.items import BodySnatchPotion, Bomb, Explosion, Heart, Item from ..entities.monsters import GiantSeaEagle, Hedgehog, Rabbit, \ TeddyBear, Tiger @@ -45,18 +45,19 @@ class TestEntities(unittest.TestCase): """ entity = Tiger() self.map.add_entity(entity) - self.assertEqual(entity.maxhealth, 20) + self.assertEqual(entity.maxhealth, 30) self.assertEqual(entity.maxhealth, entity.health) - self.assertEqual(entity.strength, 2) - for _ in range(9): + self.assertEqual(entity.strength, 5) + for _ in range(5): self.assertEqual(entity.hit(entity), - "Tiger hits tiger. Tiger takes 2 damage.") + "Tiger hits tiger. Tiger takes 5 damage.") self.assertFalse(entity.dead) self.assertEqual(entity.hit(entity), "Tiger hits tiger. " - + "Tiger takes 2 damage. Tiger dies.") + + "Tiger takes 5 damage. Tiger dies.") self.assertTrue(entity.dead) entity = Rabbit() + entity.health = 15 entity.critical = 0 self.map.add_entity(entity) entity.move(15, 44) @@ -94,7 +95,20 @@ class TestEntities(unittest.TestCase): self.assertTrue(entity.dead) self.assertGreaterEqual(self.player.current_xp, 3) - # Test the familiars + # Test that a chest is destroyed by a bomb + bomb = Bomb() + bomb.owner = self.player + bomb.move(3, 6) + self.map.add_entity(bomb) + chest = Chest() + chest.move(4, 6) + self.map.add_entity(chest) + bomb.exploding = True + for _ in range(5): + self.map.tick(self.player) + self.assertTrue(chest.annihilated) + + def test_familiar(self) -> None: fam = Trumpet() entity = Rabbit() self.map.add_entity(entity) @@ -266,6 +280,15 @@ class TestEntities(unittest.TestCase): player_state = player.save_state() self.assertEqual(player_state["current_xp"], 10) + player = Player() + player.map = self.map + player.add_xp(700) + for _ in range(13): + player.level_up() + self.assertEqual(player.level, 12) + self.assertEqual(player.critical, 5 + 95 // 30) + self.assertEqual(player.charisma, 3) + def test_critical_hit(self) -> None: """ Ensure that critical hits are working. diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index b9de160..a3c3a87 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -160,6 +160,9 @@ class TestGame(unittest.TestCase): KeyValues.SPACE) self.assertEqual(KeyValues.translate_key('plop', self.game.settings), None) + self.assertEqual(KeyValues.translate_key( + self.game.settings.KEY_DANCE, self.game.settings), + KeyValues.DANCE) def test_key_press(self) -> None: """ @@ -249,6 +252,30 @@ class TestGame(unittest.TestCase): self.game.handle_key_pressed(KeyValues.WAIT) self.assertNotIn(explosion, self.game.map.entities) + rabbit = Rabbit() + self.game.map.add_entity(rabbit) + self.game.player.move(1, 6) + rabbit.move(3, 6) + self.game.player.charisma = 11 + self.game.handle_key_pressed(KeyValues.DANCE) + self.assertEqual(rabbit.confused, 1) + string = rabbit.hit(self.game.player) + self.assertEqual(string, + "{name} is confused, it can not hit {opponent}." + .format(name=_(rabbit.translated_name.capitalize() + ), opponent=_( + self.game.player.translated_name + ))) + rabbit.confused = 0 + self.game.player.charisma = 0 + self.game.handle_key_pressed(KeyValues.DANCE) + self.assertEqual(rabbit.confused, 0) + rabbit.die() + + self.game.player.charisma = 11 + self.game.handle_key_pressed(KeyValues.DANCE) + self.game.player.charisma = 1 + self.game.handle_key_pressed(KeyValues.SPACE) self.assertEqual(self.game.state, GameMode.MAINMENU) @@ -350,7 +377,7 @@ class TestGame(unittest.TestCase): self.assertEqual(self.game.settings.KEY_LEFT_PRIMARY, 'a') # Navigate to "texture pack" - for ignored in range(13): + for ignored in range(14): self.game.handle_key_pressed(KeyValues.DOWN) # Change texture pack From 1795d622945df4fdfe6f4d3693c3bdfd11347e65 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 18:09:09 +0100 Subject: [PATCH 32/52] Update merchant prices --- docs/entities/friendly.rst | 18 +++++++++++++----- squirrelbattle/entities/items.py | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/entities/friendly.rst b/docs/entities/friendly.rst index 9fca40f..fdea174 100644 --- a/docs/entities/friendly.rst +++ b/docs/entities/friendly.rst @@ -42,13 +42,21 @@ En interagissant avec un marchand, il est possible de lui acheter et de lui vendre différents objets contre des Hazels, la monnaie du jeu. Les prix sont fixés : +* Anneau de coup critique : 15 Hazels +* Anneau d'expérience : 25 Hazels +* Arc : 22 Hazels +* Baguette de feu : 36 Hazels * Bombe : 4 Hazels -* Coeur : 3 Hazels -* Potion d'arrachage de corps : 14 Hazels -* Épée : 20 Hazels * Bouclier : 16 Hazels * Casque : 18 Hazels +* Coeur : 3 Hazels +* Épée : 20 Hazels +* Monocle : 10 Hazels +* Parchemin de dégâts : 18 Hazels +* Parchemin de faiblesse : 13 Hazels * Plastron : 30 Hazels +* Potion d'arrachage de corps : 14 Hazels +* Règle : 2 Hazels Le marchand commence avec 75 Hazels en sa possession, contre 42 pour le joueur. @@ -59,11 +67,11 @@ Dans le `pack de textures`_ écureuil, il est représenté par l'émoji ``🦜`` Trompette --------- -Son nom est fixé à 'trumpet'. Une trompette est un familier, c'est à dire que +Son nom est fixé à `trumpet`. Une trompette est un familier, c'est à dire que c'est une entité attaquante qui suit globalement le joueurs et attaque les monstres qui se rapprochent trop du joueur. -Elle a 20 point de vie et une attaque de 3. +Elle a 20 points de vie et une attaque de 3. Dans le `pack de textures`_ ASCII, elle est représentée par le caractère ``/``. diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 94a9e36..f96381b 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -521,7 +521,7 @@ class ScrollofWeakening(Item): self.held_by.inventory.remove(self) -class LongRangeWeapon(Item): +class LongRangeWeapon(Weapon): def __init__(self, damage: int = 4, rang: int = 3, *args, **kwargs): super().__init__(*args, **kwargs) From 82421be8bb212400076a4c2ca3af9839ca717c0b Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 18:16:01 +0100 Subject: [PATCH 33/52] Documentation on monocles --- docs/entities/items.rst | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/entities/items.rst b/docs/entities/items.rst index e4f1c0a..57f6a49 100644 --- a/docs/entities/items.rst +++ b/docs/entities/items.rst @@ -100,8 +100,8 @@ Plastron Le plastron est un objet que l'on peut trouver uniquement par achat auprès d'un marchand pour le coût de 30 Hazels. Il s'équippe sur le corps. -Une fois équipé, le casque ajoute 4 de -constitution à son porteur, lui permettant de prendre moins de dêgats. +Une fois équipé, le casque ajoute 4 de constitution à son porteur, +lui permettant de prendre moins de dêgats. Il est représenté par les caractères ``(`` et ``🦺``. @@ -118,4 +118,15 @@ Il y a plusieurs types d'anneaux : * **Anneau de coup critique**, qui augmente la chance de coup critique de 20%. Il coute 15 Hazels. * **Anneau de gain d'expérience amélioré**, qui multiplie le gain d'expérience du joueur par 2. Il coûte 25 Hazels. -Un anneau est représenté par les caractères ``o`` et ``💍``. \ No newline at end of file +Un anneau est représenté par les caractères ``o`` et ``💍``. + +Monocle +------- + +L'anneau est un objet que l'on peut trouver uniquement par achat auprès d'un +marchand pour le prix de 10 Hazels. Il s'équippe sur la main secondaire. + +Une fois porté, il permet de voir les caractéristiques des entités voisines +(nom, force, chance de critique, ...). + +Un monocle est représenté par les caractères ``ô`` et ``🧐``. From e96c50e81a3833060005d514a31eb999b3373c4e Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 18:36:46 +0100 Subject: [PATCH 34/52] Doc for new items. --- docs/entities/items.rst | 73 +++++++++++++++++++++++--------- squirrelbattle/entities/items.py | 2 +- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/docs/entities/items.rst b/docs/entities/items.rst index e4f1c0a..768c8dd 100644 --- a/docs/entities/items.rst +++ b/docs/entities/items.rst @@ -29,10 +29,10 @@ Bombe Une bombe est un objet que l'on peut ramasser. Une fois ramassée, elle est placée dans l'inventaire. Le joueur peut ensuite utiliser la bombe, via l'inventaire -ou après l'avoir équipée, qui fera alors 3 dégâts à toutes les +ou après l'avoir équipée, qui fera alors 5 dégâts à toutes les `entités attaquantes`_ situées à moins de trois cases au bout de 4 ticks de jeu. -Elle est représentée dans le `pack de textures`_ ASCII par le caractère ``o`` +Elle est représentée dans le `pack de textures`_ ASCII par le caractère ``ç`` et dans le `pack de textures`_ écureuil par l'émoji ``💣``. Lors de l'explosion, la bombe est remplacée par un symbole ``%`` ou l'émoji ``💥`` selon le pack de textures utilisé. @@ -44,8 +44,7 @@ Cœur ---- Un cœur est un objet que l'on ne peut pas ramasser. Dès que le joueur s'en -approche ou qu'il l'achète auprès d'un marchand, il est régénéré automatiquement -de 3 points de vie, et le cœur disparaît. +approche ou qu'il l'achète auprès d'un marchand, il récupère automatiquement 5 points de vie, et le cœur disparaît. Il est représenté dans le `pack de textures`_ ASCII par le caractère ``❤`` et dans le `pack de textures`_ écureuil par l'émoji ``💜``. @@ -65,11 +64,21 @@ Elle est représentée par les caractères ``I`` et ``🔀`` Cette potion coûte 14 Hazels auprès des marchands. +Règle +----- + +La règle est une arme que l'on peut trouver uniquement par achat auprès d'un +marchand pour le coût de 2 Hazels ou dans un coffre. Une fois équipée, la règle ajoute 1 de force +à son porteur. + +Elle est représentée par les caractères ``\`` et ``📏``. + + Épée ---- -L'épée est un objet que l'on peut trouver uniquement par achat auprès d'un -marchand pour le coût de 20 Hazels. Une fois équipée, l'épée ajoute 3 de force +L'épée est une arme que l'on peut trouver uniquement par achat auprès d'un +marchand pour le coût de 20 Hazels ou dans un coffre. Une fois équipée, l'épée ajoute 3 de force à son porteur. Elle est représentée par les caractères ``†`` et ``🗡️``. @@ -78,38 +87,32 @@ Elle est représentée par les caractères ``†`` et ``🗡️``. Bouclier -------- -Le bouclier est un objet que l'on peut trouver uniquement par achat auprès d'un -marchand pour le coût de 16 Hazels. Il s'équippe dans la main secondaire. -Une fois équipé, le bouclier ajoute 1 de -constitution à son porteur, lui permettant de parer mieux les coups. +Le bouclier est un type d'armure que l'on peut trouver uniquement par achat auprès d'un marchand pour le coût de 16 Hazels ou dans un coffre. Il s'équippe dans la main secondaire. +Une fois équipé, le bouclier ajoute 2 de constitution à son porteur, lui permettant de parer mieux les coups. Il est représenté par les caractères ``D`` et ``🛡️``. Casque ------ -Le casque est un objet que l'on peut trouver uniquement par achat auprès d'un -marchand pour le coût de 18 Hazels. Il s'équippe sur la tête. -Une fois équipé, le casque ajoute 2 de -constitution à son porteur, lui permettant de prendre moins de dêgats. +Le casque est un type d'armure que l'on peut trouver uniquement par achat auprès d'un marchand pour le coût de 18 Hazels ou dans un coffre. Il s'équippe sur la tête. +Une fois équipé, le casque ajoute 2 de constitution à son porteur, lui permettant de prendre moins de dégâts. Il est représenté par les caractères ``0`` et ``⛑️``. Plastron -------- -Le plastron est un objet que l'on peut trouver uniquement par achat auprès d'un -marchand pour le coût de 30 Hazels. Il s'équippe sur le corps. -Une fois équipé, le casque ajoute 4 de -constitution à son porteur, lui permettant de prendre moins de dêgats. +Le plastron est un type d'armure que l'on peut trouver uniquement par achat auprès d'un marchand pour le coût de 30 Hazels ou dans un coffre. Il s'équippe sur le corps. +Une fois équipé, le casque ajoute 4 de constitution à son porteur, lui permettant de prendre moins de dégâts. Il est représenté par les caractères ``(`` et ``🦺``. Anneau ------ -L'anneau est un objet que l'on peut trouver uniquement par achat auprès d'un -marchand. Il s'équippe sur la main secondaire. +Un anneau est un objet que l'on peut trouver uniquement par achat auprès d'un +marchand ou dans un coffre. Il s'équippe sur la main secondaire. Une fois équipé, l'anneau ajoute un bonus à une ou plusieurs statistiques du joueur, améliorant sa capacité à se débarasser des monstres. @@ -118,4 +121,32 @@ Il y a plusieurs types d'anneaux : * **Anneau de coup critique**, qui augmente la chance de coup critique de 20%. Il coute 15 Hazels. * **Anneau de gain d'expérience amélioré**, qui multiplie le gain d'expérience du joueur par 2. Il coûte 25 Hazels. -Un anneau est représenté par les caractères ``o`` et ``💍``. \ No newline at end of file +Un anneau est représenté par les caractères ``o`` et ``💍``. + +Parchemin +--------- + +Un parchemin est un objet consommable qui se trouve chez un marchand ou dans un coffre. Lorsqu'il est utilisé, il a un effet sur les statistiques du joueur ou des autres entités combattantes. L'intensité de l'effet du parchemin dépend de l'intelligence du joueur. + +Il y a plusieurs types de parchemins : + +* **Parchemin de dégâts**, qui inflige des dégâts à toutes les entités combattantes qui sont à distance moins de 5 du joueur (ça touche aussi les familiers, mais pas le joueur). Le nombre de points de dégâts est directement l'intelligence du joueur. Il coute 18 Hazels. +* **Parchemin de faiblesse**, qui réduit la force de toutes les entités sauf le joueur de min(1, intelligence//2) pendant 3 tours du jeu. Il coûte 13 Hazels. + +Un parchemin est représenté par les caractères ``]`` et ``📜``. + +Arc +--- + +Un arc est une arme à distance qui s'équippe dans la main principale. Pour l'utiliser, il faut appuyer sur la touche de lancer (l de base) puis une touche de direction. Une flèche est alors tirée dans cette direction, et touche le premier ennemi qu'elle rencontre, s'il existe, sur les 3 premières cases dans cette direction. + +La flèche fait 4 + dextérité du joueur dégâts. +L'arc coûte 22 Hazels chez un marchand. On peut le trouver sinon dans les coffres. + +Baton de boule de feu +--------------------- + +Un baton est une arme à distance qui s'équippe dans la main principale. Pour l'utiliser, il faut appuyer sur la touche de lancer (l de base) puis une touche de direction. Une boule de feu est alors tirée dans cette direction, et touche le premier ennemi qu'elle rencontre, s'il existe, sur les 4 premières cases dans cette direction. Lorsqu'un ennemi est touché, une explosion est affichée sur sa case. + +La flèche fait 6 + intelligence du joueur dégâts. +Le baton coûte 36 Hazels chez un marchand. On peut le trouver sinon dans les coffres. \ No newline at end of file diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index ac502ea..2741033 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -613,7 +613,7 @@ class FireBallStaff(LongRangeWeapon): @property def stat(self) -> str: """ - Here it is dexterity + Here it is intelligence """ return "intelligence" From 258bd0d81626e8429497937f53aa8c48abc358f2 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 18:38:56 +0100 Subject: [PATCH 35/52] re-itme doc --- docs/entities/items.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/entities/items.rst b/docs/entities/items.rst index 768c8dd..c9565e1 100644 --- a/docs/entities/items.rst +++ b/docs/entities/items.rst @@ -143,10 +143,14 @@ Un arc est une arme à distance qui s'équippe dans la main principale. Pour l'u La flèche fait 4 + dextérité du joueur dégâts. L'arc coûte 22 Hazels chez un marchand. On peut le trouver sinon dans les coffres. +Il est représenté par les caractères ``)`` et ``🏹``. + Baton de boule de feu --------------------- Un baton est une arme à distance qui s'équippe dans la main principale. Pour l'utiliser, il faut appuyer sur la touche de lancer (l de base) puis une touche de direction. Une boule de feu est alors tirée dans cette direction, et touche le premier ennemi qu'elle rencontre, s'il existe, sur les 4 premières cases dans cette direction. Lorsqu'un ennemi est touché, une explosion est affichée sur sa case. -La flèche fait 6 + intelligence du joueur dégâts. -Le baton coûte 36 Hazels chez un marchand. On peut le trouver sinon dans les coffres. \ No newline at end of file +La boule de feu fait 6 + intelligence du joueur dégâts. +Le baton coûte 36 Hazels chez un marchand. On peut le trouver sinon dans les coffres. + +Il est représenté par les caractères ``:`` et ``🪄``. \ No newline at end of file From bc638b3becf49c52a004c7b0bfb278df9a7262d2 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 18:42:26 +0100 Subject: [PATCH 36/52] Monocles can be found in chests --- docs/entities/items.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/entities/items.rst b/docs/entities/items.rst index 57f6a49..9ca1b39 100644 --- a/docs/entities/items.rst +++ b/docs/entities/items.rst @@ -124,7 +124,8 @@ Monocle ------- L'anneau est un objet que l'on peut trouver uniquement par achat auprès d'un -marchand pour le prix de 10 Hazels. Il s'équippe sur la main secondaire. +marchand pour le prix de 10 Hazels. On peut le trouver sinon dans les coffres. +Il s'équippe sur la main secondaire. Une fois porté, il permet de voir les caractéristiques des entités voisines (nom, force, chance de critique, ...). From c9994423cb8685df2ec9f58fe61a77bb7eb1e6b2 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 18:44:17 +0100 Subject: [PATCH 37/52] Added dance to doc. --- docs/entities/player.rst | 13 ++++++++++++- squirrelbattle/entities/player.py | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/entities/player.rst b/docs/entities/player.rst index d3a644a..fc0d12c 100644 --- a/docs/entities/player.rst +++ b/docs/entities/player.rst @@ -32,6 +32,10 @@ Déplacement Selon les paramètres_, il est possible de bouger le joueur dans les 4 directions en appuyant sur ``z``, ``q``, ``s``, ``d`` ou sur les flèches directionnelles. +(ou sur d'autres touches selon ce qui est écrit dans le menu des paramètres) + +Le joueur peut aussi ne rien faire pendant son tour, il suffit d'appuyer sur +la touche d'attente (``w`` de base). Le joueur se retrouvera bloqué s'il avance contre un mur. Si il avance sur un objet_, alors il prend l'objet_ et avance sur la case. @@ -40,6 +44,11 @@ S'il rencontre une autre `entité attaquante`_, alors il frappe l'entité en infligeant autant de dégâts qu'il n'a de force. À chaque fois qu'une entité est tuée, le joueur gagne aléatoirement entre 3 et 7 points d'expérience. +Outre se déplacer et attaquer, le joueur peut utiliser la touche pour danser +(``y`` de base) durant son tour et danser. Selon son charisme, il a plus ou moins +de chances de rendre confus tous les ennemis à distance moins de 3. Un ennemi confus +ne peut pas attaquer. + Expérience ---------- @@ -49,4 +58,6 @@ Lorsque le joueur atteint la quantité d'expérience requise pour monter de nive le joueur gagne un niveau, regagne toute sa vie, consomme son expérience et la nouvelle quantité d'expérience requise est 10 fois le niveau actuel. De plus, entre 5 et 10 fois le niveau actuel entités apparaissent aléatoirement sur la -carte à la montée de niveau. Enfin, le joueur gagne en force en montant de niveau. +carte à la montée de niveau. Enfin, le joueur améliore ses statistiques en augmentant +de niveau. Toutes les caractéristiques ne sont pas incrémentées à chaque niveau +gagné. diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 7648639..a241fef 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -66,7 +66,7 @@ class Player(InventoryHolder, FightingEntity): def dance(self) -> None: """ Dancing has a certain probability or making ennemies unable - to fight for 2 turns. That probability depends on the player's + to fight for 3 turns. That probability depends on the player's charisma. """ diceroll = randint(1, 10) From 2b63f8b3f154060de20c517d9b4d700a4a41260f Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 18:54:36 +0100 Subject: [PATCH 38/52] Added stats in doc. --- docs/entities/player.rst | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/entities/player.rst b/docs/entities/player.rst index fc0d12c..62f1e3c 100644 --- a/docs/entities/player.rst +++ b/docs/entities/player.rst @@ -5,6 +5,9 @@ Joueur .. _`paramètres`: ../settings.html .. _`pack de textures`: ../texture-pack.html .. _`objet`: items.html +.. _`parchemins`: items.html#Parchemin +.. _`batons` : items.html#Baton de boule de feu +.. _`arc` : items.html#Arc Le joueur est une `entité attaquante`_, contrôlée par l'utilisateur humain. @@ -50,6 +53,20 @@ de chances de rendre confus tous les ennemis à distance moins de 3. Un ennemi c ne peut pas attaquer. +Statistiques +------------ + +Le joueur possède plusieurs statistiques : + +* Niveau : son niveau, qui dépend de combien d'expérience il a accumulé +* Expérience : la quantité d'expérience accumulée par le joueur, qui dépend de combien d'entités il a tué. +* Force : indique combien de dommages le joueur inflige à ses ennemis +* Intelligence : joue sur l'effet des objets magiques, tels que les `parchemins`_ ou les `batons`_ +* Charisme : joue sur l'efficacité de la danse du joueur +* Dextérité : joue sur l'efficacité de l'`arc`_ +* Constitution : joue sur la quantité de dégâts que le joueur prend lorsqu'un monstre le frappe +* Taux de critique : la chance (en pourcentage) que le joueur a de faire un coup critique + Expérience ---------- @@ -58,6 +75,4 @@ Lorsque le joueur atteint la quantité d'expérience requise pour monter de nive le joueur gagne un niveau, regagne toute sa vie, consomme son expérience et la nouvelle quantité d'expérience requise est 10 fois le niveau actuel. De plus, entre 5 et 10 fois le niveau actuel entités apparaissent aléatoirement sur la -carte à la montée de niveau. Enfin, le joueur améliore ses statistiques en augmentant -de niveau. Toutes les caractéristiques ne sont pas incrémentées à chaque niveau -gagné. +carte à la montée de niveau. Enfin, le joueur améliore ses statistiques en augmentant de niveau. Toutes les caractéristiques ne sont pas incrémentées à chaque niveau gagné. From cddff5c2d9943eab747666d1e8b4bd59eeb72f43 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 19:17:04 +0100 Subject: [PATCH 39/52] Fix linting --- squirrelbattle/enums.py | 4 ++-- squirrelbattle/tests/game_test.py | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/squirrelbattle/enums.py b/squirrelbattle/enums.py index 42bd643..b61f236 100644 --- a/squirrelbattle/enums.py +++ b/squirrelbattle/enums.py @@ -53,7 +53,8 @@ class KeyValues(Enum): DANCE = auto() @staticmethod - def translate_key(key: str, settings: Settings) -> Optional["KeyValues"]: + def translate_key(key: str, settings: Settings) \ + -> Optional["KeyValues"]: # noqa: C901 """ Translates the raw string key into an enum value that we can use. """ @@ -91,4 +92,3 @@ class KeyValues(Enum): return KeyValues.LAUNCH elif key == settings.KEY_DANCE: return KeyValues.DANCE - return None diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index a3c3a87..895af12 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -260,12 +260,10 @@ class TestGame(unittest.TestCase): self.game.handle_key_pressed(KeyValues.DANCE) self.assertEqual(rabbit.confused, 1) string = rabbit.hit(self.game.player) - self.assertEqual(string, - "{name} is confused, it can not hit {opponent}." - .format(name=_(rabbit.translated_name.capitalize() - ), opponent=_( - self.game.player.translated_name - ))) + self.assertEqual( + string, _("{name} is confused, it can not hit {opponent}.") + .format(name=rabbit.translated_name.capitalize(), + opponent=self.game.player.translated_name)) rabbit.confused = 0 self.game.player.charisma = 0 self.game.handle_key_pressed(KeyValues.DANCE) From 01ee49ddd44a12b0aa0bad39a6b01c76185b7b62 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 19:25:12 +0100 Subject: [PATCH 40/52] Entities are updated after a bow shot, fixes #70 --- squirrelbattle/entities/items.py | 11 ++++++----- squirrelbattle/game.py | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index 182c86b..ac5f74d 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -47,7 +47,7 @@ class Item(Entity): Indicates what should be done when the item is used. """ - def throw(self, direction: int) -> None: + def throw(self, direction: int) -> Any: """ Indicates what should be done when the item is thrown. """ @@ -555,7 +555,7 @@ class LongRangeWeapon(Weapon): self.held_by, self.damage + getattr(self.held_by, self.stat)) self.held_by.map.logs.add_message(line) - return (to_kill.x, to_kill.y) if to_kill else None + return (to_kill.y, to_kill.x) if to_kill else None def equip(self) -> None: """ @@ -621,17 +621,18 @@ class FireBallStaff(LongRangeWeapon): def string(self) -> str: return " is shot by a fire ball." - def throw(self, direction: int) -> None: + def throw(self, direction: int) -> Any: """ Adds an explosion animation when killing something. """ coord = super().throw(direction) if coord: - x = coord[0] - y = coord[1] + y = coord[0] + x = coord[1] explosion = Explosion(y=y, x=x) self.held_by.map.add_entity(explosion) + return y, x class Monocle(Item): diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index bde825f..83da5f3 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -292,7 +292,8 @@ class Game: return if self.player.equipped_main: - self.player.equipped_main.throw(direction) + if self.player.equipped_main.throw(direction): + self.map.tick(self.player) def handle_key_pressed_inventory(self, key: KeyValues) -> None: """ From 16ddbd31f305e897d60a81a4fc847738ed1dd766 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Sun, 10 Jan 2021 19:41:51 +0100 Subject: [PATCH 41/52] also prevent updating seen tiles when checking vision --- squirrelbattle/interfaces.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index bf8ddbe..330301c 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -197,9 +197,11 @@ class Map: def is_visible_from(self, starty: int, startx: int, desty: int, destx: int, max_range: int) -> bool: oldvisibility = deepcopy(self.visibility) + oldseen = deepcopy(self.seen_tiles) self.compute_visibility(starty, startx, max_range) result = self.visibility[desty][destx] self.visibility = oldvisibility + self.seen_tiles = oldseen return result def compute_visibility(self, y: int, x: int, max_range: int) -> None: From 5694dd4dff20d5aa9da90344ebe2e4e49a38bed4 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 19:52:28 +0100 Subject: [PATCH 42/52] Update the player inventory when opening the menu/loading the game, fixes #72 --- squirrelbattle/display/menudisplay.py | 1 + squirrelbattle/game.py | 1 + 2 files changed, 2 insertions(+) diff --git a/squirrelbattle/display/menudisplay.py b/squirrelbattle/display/menudisplay.py index 54acaed..ccc3561 100644 --- a/squirrelbattle/display/menudisplay.py +++ b/squirrelbattle/display/menudisplay.py @@ -162,6 +162,7 @@ class PlayerInventoryDisplay(MenuDisplay): def update(self, game: Game) -> None: self.player = game.player self.update_menu(game.inventory_menu) + game.inventory_menu.update_player(self.player) self.store_mode = game.state == GameMode.STORE self.chest_mode = game.state == GameMode.CHEST self.selected = game.state == GameMode.INVENTORY \ diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 83da5f3..9cfa721 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -435,6 +435,7 @@ class Game: return self.player = players[0] + self.inventory_menu.update_player(self.player) self.map.compute_visibility(self.player.y, self.player.x, self.player.vision) self.display_actions(DisplayActions.UPDATE) From 3758cb13361682922eadfba3ab6be8b6c69889cf Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 20:20:12 +0100 Subject: [PATCH 43/52] Fix the GiantSeaEagle registration, fixes #76 --- squirrelbattle/game.py | 4 ++-- squirrelbattle/interfaces.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 9cfa721..f59f676 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -419,9 +419,9 @@ class Game: try: self.map_index = d["map_index"] self.maps = [Map().load_state(map_dict) for map_dict in d["maps"]] - except KeyError: + except KeyError as error: self.message = _("Some keys are missing in your save file.\n" - "Your save seems to be corrupt. It got deleted.") + "Your save seems to be corrupt. It got deleted.") + f"\n{error}" os.unlink(ResourceManager.get_config_path("save.json")) self.display_actions(DisplayActions.UPDATE) return diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 330301c..14fcc6d 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -662,8 +662,8 @@ class Entity: "Bow": Bow, "Chest": Chest, "Chestplate": Chestplate, - "Eagle": GiantSeaEagle, "FireBallStaff": FireBallStaff, + "GiantSeaEagle": GiantSeaEagle, "Heart": Heart, "Hedgehog": Hedgehog, "Helmet": Helmet, From 67a9bda6e180aa95907eefc0a406fc2bc6a4ab27 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 20:46:01 +0100 Subject: [PATCH 44/52] Don't fill the inventory of a chest or a Merchant if it is empty, fixes #73 --- squirrelbattle/entities/friendly.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 57506e9..1a92392 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -24,9 +24,11 @@ class Merchant(InventoryHolder, FriendlyEntity): def __init__(self, name: str = "merchant", inventory: list = None, hazel: int = 75, maxhealth: int = 8, *args, **kwargs): super().__init__(name=name, maxhealth=maxhealth, *args, **kwargs) - self.inventory = self.translate_inventory(inventory or []) + self.inventory = self.translate_inventory(inventory) \ + if inventory is not None else None self.hazel = hazel - if not self.inventory: + if self.inventory is None: + self.inventory = [] for i in range(5): self.inventory.append(choice(Item.get_all_items())()) @@ -54,9 +56,11 @@ class Chest(InventoryHolder, FriendlyEntity): hazel: int = 0, *args, **kwargs): super().__init__(name=name, *args, **kwargs) self.hazel = hazel - self.inventory = self.translate_inventory(inventory or []) + self.inventory = self.translate_inventory(inventory) \ + if inventory is not None else None self.annihilated = False - if not self.inventory: + if self.inventory is None: + self.inventory = [] for i in range(3): self.inventory.append(choice(Item.get_all_items())()) @@ -84,7 +88,6 @@ class Chest(InventoryHolder, FriendlyEntity): """ return self.annihilated - class Sunflower(FriendlyEntity): """ A friendly sunflower. From d2af345c0ca4e8cce2182482d32ee357ba96f2f0 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 21:01:43 +0100 Subject: [PATCH 45/52] Fix linting --- squirrelbattle/entities/friendly.py | 1 + squirrelbattle/game.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/entities/friendly.py b/squirrelbattle/entities/friendly.py index 1a92392..9b149de 100644 --- a/squirrelbattle/entities/friendly.py +++ b/squirrelbattle/entities/friendly.py @@ -88,6 +88,7 @@ class Chest(InventoryHolder, FriendlyEntity): """ return self.annihilated + class Sunflower(FriendlyEntity): """ A friendly sunflower. diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index f59f676..44c1f48 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -421,7 +421,8 @@ class Game: self.maps = [Map().load_state(map_dict) for map_dict in d["maps"]] except KeyError as error: self.message = _("Some keys are missing in your save file.\n" - "Your save seems to be corrupt. It got deleted.") + f"\n{error}" + "Your save seems to be corrupt. It got deleted.")\ + + f"\n{error}" os.unlink(ResourceManager.get_config_path("save.json")) self.display_actions(DisplayActions.UPDATE) return From a1d69203c9c5dbc4bec2954d57c5a656a0725659 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 21:15:30 +0100 Subject: [PATCH 46/52] Translations. --- .../locale/de/LC_MESSAGES/squirrelbattle.po | 197 ++++++++++------- .../locale/es/LC_MESSAGES/squirrelbattle.po | 202 +++++++++++------- .../locale/fr/LC_MESSAGES/squirrelbattle.po | 154 ++++++++----- 3 files changed, 345 insertions(+), 208 deletions(-) diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index 2cea0a3..1aed270 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -1,3 +1,13 @@ +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} nimmt {amount} Schadenspunkte." + +msgid "ring_of_more_experience" +msgstr "" + +msgid "ring_of_critical_damage" +msgstr "" + # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao # This file is distributed under the same license as the squirrelbattle package. @@ -8,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-08 15:15+0100\n" +"POT-Creation-Date: 2021-01-10 20:35+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,116 +27,146 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -msgid "ring_of_critical_damage" -msgstr "" - -msgid "ring_of_more_experience" -msgstr "" - -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} nimmt {amount} Schadenspunkte." - -#: squirrelbattle/display/creditsdisplay.py:28 -#: squirrelbattle/display/menudisplay.py:123 -#: squirrelbattle/display/menudisplay.py:148 -msgid "Credits" -msgstr "" - -#: squirrelbattle/display/creditsdisplay.py:32 -msgid "Developers:" -msgstr "" - -#: squirrelbattle/display/creditsdisplay.py:38 -msgid "Translators:" -msgstr "" - -#: squirrelbattle/display/menudisplay.py:168 -msgid "INVENTORY" -msgstr "BESTAND" - -#: squirrelbattle/display/menudisplay.py:214 -msgid "STALL" -msgstr "STAND" - -#: squirrelbattle/display/statsdisplay.py:44 +#: squirrelbattle/display/gamedisplay.py:150 msgid "Inventory:" msgstr "Bestand:" -#: squirrelbattle/display/statsdisplay.py:61 +#: squirrelbattle/display/gamedisplay.py:167 msgid "Equipped main:" -msgstr "" +msgstr "Hauptausgestattete Ding" -#: squirrelbattle/display/statsdisplay.py:65 +#: squirrelbattle/display/gamedisplay.py:171 msgid "Equipped secondary:" -msgstr "" +msgstr "zusätzlich Ausgestattete Ding" -#: squirrelbattle/display/statsdisplay.py:70 +#: squirrelbattle/display/gamedisplay.py:176 msgid "Equipped chestplate:" -msgstr "" +msgstr "Ausgestattet Harnisch" -#: squirrelbattle/display/statsdisplay.py:74 +#: squirrelbattle/display/gamedisplay.py:180 msgid "Equipped helmet:" -msgstr "" +msgstr "Ausgestattet Helm" -#: squirrelbattle/display/statsdisplay.py:81 +#: squirrelbattle/display/gamedisplay.py:187 msgid "YOU ARE DEAD" msgstr "SIE WURDEN GESTORBEN" -#: squirrelbattle/display/statsdisplay.py:85 +#: squirrelbattle/display/gamedisplay.py:191 #, python-brace-format msgid "Use {key} to use the ladder" -msgstr "" +msgstr "Nutzen {key} um die Leiter zu nutzen" -#: squirrelbattle/display/statsdisplay.py:94 +#: squirrelbattle/display/gamedisplay.py:210 msgid "Move to the friendly entity to talk to it" -msgstr "" +msgstr "Ziehen Sie zu der freundlichen Einheit hin, um mit ihr zu sprechen" -#: squirrelbattle/display/statsdisplay.py:96 +#: squirrelbattle/display/gamedisplay.py:212 #, python-brace-format msgid "Use {key} then move to talk to the entity" -msgstr "" +msgstr "Verwenden Sie {key} dann bewegen Sie sich, um mit der Einheit zu sprechen" + +#: squirrelbattle/display/menudisplay.py:124 +#: squirrelbattle/display/menudisplay.py:149 +#: squirrelbattle/display/menudisplay.py:304 +msgid "Credits" +msgstr "Abspann" + +#: squirrelbattle/display/menudisplay.py:173 +msgid "INVENTORY" +msgstr "BESTAND" + +#: squirrelbattle/display/menudisplay.py:219 +msgid "STALL" +msgstr "STAND" + +#: squirrelbattle/display/menudisplay.py:263 +msgid "CHEST" +msgstr "KASTE" + +#: squirrelbattle/display/menudisplay.py:308 +msgid "Developers:" +msgstr "Entwickler:" + +#: squirrelbattle/display/menudisplay.py:314 +msgid "Translators:" +msgstr "Ubersetzer:" #. TODO -#: squirrelbattle/entities/friendly.py:33 +#: squirrelbattle/entities/friendly.py:38 msgid "I don't sell any squirrel" msgstr "Ich verkaufe keinen Eichhörnchen." -#: squirrelbattle/entities/friendly.py:55 +#: squirrelbattle/entities/friendly.py:68 +msgid "You have opened the chest" +msgstr "Sie haben der Kaste geöffnet" + +#: squirrelbattle/entities/friendly.py:77 +msgid "The chest exploded" +msgstr "" + +#: squirrelbattle/entities/friendly.py:78 +msgid "It's not really effective" +msgstr "Es ist nicht wirklich effektiv" + +#: squirrelbattle/entities/friendly.py:101 msgid "Flower power!!" msgstr "Blumenmacht!!" -#: squirrelbattle/entities/friendly.py:55 +#: squirrelbattle/entities/friendly.py:101 msgid "The sun is warm today" msgstr "Die Sonne ist warm heute" #. The bomb is exploding. #. Each entity that is close to the bomb takes damages. #. The player earn XP if the entity was killed. -#: squirrelbattle/entities/items.py:178 +#: squirrelbattle/entities/items.py:189 msgid "Bomb is exploding." msgstr "Die Bombe explodiert." -#: squirrelbattle/entities/items.py:365 +#: squirrelbattle/entities/items.py:385 #, python-brace-format msgid "{player} exchanged its body with {entity}." msgstr "{player} täuscht seinem Körper mit {entity} aus." -#: squirrelbattle/game.py:200 +#: squirrelbattle/entities/items.py:519 +msgid "" +"The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 " +"turns" +msgstr "Die Feinde haben 3 Runden lang - {max(1, self.held_by.intelligence // 2)} Stärke" +"" + +#: squirrelbattle/entities/items.py:552 +#, python-brace-format +msgid "{name}" +msgstr "{name}" + +#: squirrelbattle/entities/player.py:83 +msgid "It worked! Nearby ennemies will be confused for 3 turns." +msgstr "Es funktionierte! In der Nähe befindliche Feinde werden 3 Runden lang verwirrt." + +#: squirrelbattle/entities/player.py:86 +msgid "It worked, but there is no one nearby..." +msgstr "Es hat funktioniert, aber es ist niemand in der Nähe ..." + +#: squirrelbattle/entities/player.py:89 +msgid "The dance was not effective..." +msgstr "Der Tanz war nicht effektiv ..." + +#: squirrelbattle/game.py:214 #, python-brace-format msgid "The player climbs down to the floor {floor}." msgstr "Der Spieler klettert auf dem Stock {floor} hinunter." -#: squirrelbattle/game.py:213 +#: squirrelbattle/game.py:227 #, python-brace-format msgid "The player climbs up the floor {floor}." msgstr "Der Spieler klettert auf dem Stock {floor} hinoben." -#: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603 +#: squirrelbattle/game.py:348 squirrelbattle/tests/game_test.py:631 msgid "The buyer does not have enough money" msgstr "Der Kaufer hat nicht genug Geld" -#: squirrelbattle/game.py:349 +#: squirrelbattle/game.py:423 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -134,7 +174,7 @@ msgstr "" "In Ihrer Speicherdatei fehlen einige Schlüssel.\n" "Ihre Speicherung scheint korrupt zu sein. Es wird gelöscht." -#: squirrelbattle/game.py:357 +#: squirrelbattle/game.py:431 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -142,7 +182,7 @@ msgstr "" "Auf dieser Karte wurde kein Spieler gefunden!\n" "Vielleicht sind Sie gestorben?" -#: squirrelbattle/game.py:379 +#: squirrelbattle/game.py:454 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -150,26 +190,31 @@ msgstr "" "Die JSON-Datei ist nicht korrekt.\n" "Ihre Speicherung scheint korrumpiert. Sie wurde gelöscht." -#: squirrelbattle/interfaces.py:718 -msgid "It's a critical hit!" -msgstr "" +#: squirrelbattle/interfaces.py:758 squirrelbattle/tests/game_test.py:264 +#, python-brace-format +msgid "{name} is confused, it can not hit {opponent}." +msgstr "{name} ist verwirrt, es kann {opponent} nicht schlagen." -#: squirrelbattle/interfaces.py:719 +#: squirrelbattle/interfaces.py:766 +msgid "It's a critical hit!" +msgstr "Es ist ein kritischer Treffer!" + +#: squirrelbattle/interfaces.py:767 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} schlägt {opponent}." -#: squirrelbattle/interfaces.py:733 +#: squirrelbattle/interfaces.py:781 #, python-brace-format msgid "{name} takes {damage} damage." -msgstr "" +msgstr "{name} erleidet {damage} Schaden." -#: squirrelbattle/interfaces.py:735 +#: squirrelbattle/interfaces.py:783 #, python-brace-format msgid "{name} dies." msgstr "{name} stirbt." -#: squirrelbattle/interfaces.py:769 +#: squirrelbattle/interfaces.py:817 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} hat gesagt: {message}" @@ -178,8 +223,8 @@ msgstr "{entity} hat gesagt: {message}" msgid "Back" msgstr "Zurück" -#: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371 -#: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377 +#: squirrelbattle/tests/game_test.py:395 squirrelbattle/tests/game_test.py:398 +#: squirrelbattle/tests/game_test.py:401 squirrelbattle/tests/game_test.py:404 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Neu Spiel" @@ -306,7 +351,7 @@ msgstr "Tiger" #: squirrelbattle/tests/translations_test.py:70 msgid "eagle" -msgstr "" +msgstr "Adler" #: squirrelbattle/tests/translations_test.py:72 msgid "body snatch potion" @@ -334,20 +379,20 @@ msgstr "" #: squirrelbattle/tests/translations_test.py:78 msgid "chestplate" -msgstr "" +msgstr "Brustpanzer" #: squirrelbattle/tests/translations_test.py:79 msgid "shield" -msgstr "" +msgstr "Schild" #: squirrelbattle/tests/translations_test.py:80 msgid "ring of critical damage" -msgstr "" +msgstr "Ring des kritischen Schadens" #: squirrelbattle/tests/translations_test.py:82 msgid "ring of more experience" -msgstr "" +msgstr "Ring der mehr Erfahrung" #: squirrelbattle/tests/translations_test.py:84 msgid "monocle" -msgstr "" +msgstr "Monokel" diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index 18868c3..7b19c4e 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -1,3 +1,13 @@ +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} recibe {amount} daño." + +msgid "ring_of_more_experience" +msgstr "ring_of_more_experience" + +msgid "ring_of_critical_damage" +msgstr "ring_of_critical_damage" + # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao # This file is distributed under the same license as the squirrelbattle package. @@ -8,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-08 15:15+0100\n" +"POT-Creation-Date: 2021-01-10 20:35+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,115 +27,146 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -msgid "ring_of_critical_damage" -msgstr "" - -msgid "ring_of_more_experience" -msgstr "" - -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} recibe {amount} daño." - -#: squirrelbattle/display/creditsdisplay.py:28 -#: squirrelbattle/display/menudisplay.py:123 -#: squirrelbattle/display/menudisplay.py:148 -msgid "Credits" -msgstr "" - -#: squirrelbattle/display/creditsdisplay.py:32 -msgid "Developers:" -msgstr "" - -#: squirrelbattle/display/creditsdisplay.py:38 -msgid "Translators:" -msgstr "" - -#: squirrelbattle/display/menudisplay.py:168 -msgid "INVENTORY" -msgstr "INVENTORIO" - -#: squirrelbattle/display/menudisplay.py:214 -msgid "STALL" -msgstr "PUESTO" - -#: squirrelbattle/display/statsdisplay.py:44 +#: squirrelbattle/display/gamedisplay.py:150 msgid "Inventory:" msgstr "Inventorio :" -#: squirrelbattle/display/statsdisplay.py:61 +#: squirrelbattle/display/gamedisplay.py:167 msgid "Equipped main:" -msgstr "" +msgstr "Principal equipado:" -#: squirrelbattle/display/statsdisplay.py:65 +#: squirrelbattle/display/gamedisplay.py:171 msgid "Equipped secondary:" -msgstr "" +msgstr "Equipado secundario:" -#: squirrelbattle/display/statsdisplay.py:70 +#: squirrelbattle/display/gamedisplay.py:176 msgid "Equipped chestplate:" -msgstr "" +msgstr "Pechera equipada:" -#: squirrelbattle/display/statsdisplay.py:74 +#: squirrelbattle/display/gamedisplay.py:180 msgid "Equipped helmet:" -msgstr "" +msgstr "Casco equipado:" -#: squirrelbattle/display/statsdisplay.py:81 +#: squirrelbattle/display/gamedisplay.py:187 msgid "YOU ARE DEAD" msgstr "ERES MUERTO" -#: squirrelbattle/display/statsdisplay.py:85 +#: squirrelbattle/display/gamedisplay.py:191 #, python-brace-format msgid "Use {key} to use the ladder" -msgstr "" +msgstr "Usa {key} para usar la escalera" -#: squirrelbattle/display/statsdisplay.py:94 +#: squirrelbattle/display/gamedisplay.py:210 msgid "Move to the friendly entity to talk to it" -msgstr "" +msgstr "Muévete hacia la entidad amiga para hablar con ella." -#: squirrelbattle/display/statsdisplay.py:96 +#: squirrelbattle/display/gamedisplay.py:212 #, python-brace-format msgid "Use {key} then move to talk to the entity" +msgstr "Usa {key} y luego muévete para hablar con la entidad" + +#: squirrelbattle/display/menudisplay.py:124 +#: squirrelbattle/display/menudisplay.py:149 +#: squirrelbattle/display/menudisplay.py:304 +msgid "Credits" msgstr "" -#: squirrelbattle/entities/friendly.py:33 +#: squirrelbattle/display/menudisplay.py:173 +msgid "INVENTORY" +msgstr "INVENTORIO" + +#: squirrelbattle/display/menudisplay.py:219 +msgid "STALL" +msgstr "PUESTO" + +#: squirrelbattle/display/menudisplay.py:263 +msgid "CHEST" +msgstr "COFRE" + +#: squirrelbattle/display/menudisplay.py:308 +msgid "Developers:" +msgstr "Desarrollador:" + +#: squirrelbattle/display/menudisplay.py:314 +msgid "Translators:" +msgstr "Traductores:" + +#: squirrelbattle/entities/friendly.py:38 msgid "I don't sell any squirrel" msgstr "No vendo ninguna ardilla" -#: squirrelbattle/entities/friendly.py:55 +#: squirrelbattle/entities/friendly.py:68 +msgid "You have opened the chest" +msgstr "Abriste el cofre" + +#: squirrelbattle/entities/friendly.py:77 +msgid "The chest exploded" +msgstr "El cofre explotó" + +#: squirrelbattle/entities/friendly.py:78 +msgid "It's not really effective" +msgstr "No es realmente efectivo" + +#: squirrelbattle/entities/friendly.py:101 msgid "Flower power!!" msgstr "Poder de las flores!!" -#: squirrelbattle/entities/friendly.py:55 +#: squirrelbattle/entities/friendly.py:101 msgid "The sun is warm today" msgstr "El sol está caliente hoy" #. The bomb is exploding. #. Each entity that is close to the bomb takes damages. #. The player earn XP if the entity was killed. -#: squirrelbattle/entities/items.py:178 +#: squirrelbattle/entities/items.py:189 msgid "Bomb is exploding." msgstr "La bomba está explotando." -#: squirrelbattle/entities/items.py:365 +#: squirrelbattle/entities/items.py:385 #, python-brace-format msgid "{player} exchanged its body with {entity}." msgstr "{player} intercambió su cuerpo con {entity}." -#: squirrelbattle/game.py:200 +#: squirrelbattle/entities/items.py:519 +msgid "" +"The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 " +"turns" +msgstr "" +"Los enemigos tienen - {max(1, self.held_by.intelligence // 2)} fuerza durante 3" +"turnos" + +#: squirrelbattle/entities/items.py:552 +#, python-brace-format +msgid "{name}" +msgstr "{name}" + +#: squirrelbattle/entities/player.py:83 +msgid "It worked! Nearby ennemies will be confused for 3 turns." +msgstr "¡Funcionó! Los enemigos cercanos se confundirán durante 3 turnos." + +#: squirrelbattle/entities/player.py:86 +msgid "It worked, but there is no one nearby..." +msgstr "Funcionó, pero no hay nadie cerca ..." + +#: squirrelbattle/entities/player.py:89 +msgid "The dance was not effective..." +msgstr "El baile no fue efectivo ..." + +#: squirrelbattle/game.py:214 #, python-brace-format msgid "The player climbs down to the floor {floor}." -msgstr "" +msgstr "El jugador desciende alla planta {floor}." -#: squirrelbattle/game.py:213 +#: squirrelbattle/game.py:227 #, python-brace-format msgid "The player climbs up the floor {floor}." -msgstr "" +msgstr "El jugador sube por la planta {floor}." -#: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603 +#: squirrelbattle/game.py:348 squirrelbattle/tests/game_test.py:631 msgid "The buyer does not have enough money" msgstr "El comprador no tiene suficiente dinero" -#: squirrelbattle/game.py:349 +#: squirrelbattle/game.py:423 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -133,7 +174,7 @@ msgstr "" "Algunas claves faltan en su archivo de guarda.\n" "Su guarda parece a ser corruptido. Fue eliminado." -#: squirrelbattle/game.py:357 +#: squirrelbattle/game.py:431 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -141,7 +182,7 @@ msgstr "" "No jugador encontrado sobre la carta !\n" "¿ Quizas murió ?" -#: squirrelbattle/game.py:379 +#: squirrelbattle/game.py:454 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -149,26 +190,31 @@ msgstr "" "El JSON archivo no es correcto.\n" "Su guarda parece corrupta. Fue eliminada." -#: squirrelbattle/interfaces.py:718 -msgid "It's a critical hit!" -msgstr "" +#: squirrelbattle/interfaces.py:758 squirrelbattle/tests/game_test.py:264 +#, python-brace-format +msgid "{name} is confused, it can not hit {opponent}." +msgstr "{name} está confundido, no puede golpear a {opponent}." -#: squirrelbattle/interfaces.py:719 +#: squirrelbattle/interfaces.py:766 +msgid "It's a critical hit!" +msgstr "¡Es un golpe crítico!" + +#: squirrelbattle/interfaces.py:767 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} golpea a {opponent}." -#: squirrelbattle/interfaces.py:733 +#: squirrelbattle/interfaces.py:781 #, python-brace-format msgid "{name} takes {damage} damage." -msgstr "" +msgstr "{name} recibe {damage} daño." -#: squirrelbattle/interfaces.py:735 +#: squirrelbattle/interfaces.py:783 #, python-brace-format msgid "{name} dies." msgstr "{name} se muere." -#: squirrelbattle/interfaces.py:769 +#: squirrelbattle/interfaces.py:817 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} dijo : {message}" @@ -177,8 +223,8 @@ msgstr "{entity} dijo : {message}" msgid "Back" msgstr "Volver" -#: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371 -#: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377 +#: squirrelbattle/tests/game_test.py:395 squirrelbattle/tests/game_test.py:398 +#: squirrelbattle/tests/game_test.py:401 squirrelbattle/tests/game_test.py:404 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Nuevo partido" @@ -305,7 +351,7 @@ msgstr "tigre" #: squirrelbattle/tests/translations_test.py:70 msgid "eagle" -msgstr "" +msgstr "águila" #: squirrelbattle/tests/translations_test.py:72 msgid "body snatch potion" @@ -329,24 +375,24 @@ msgstr "espada" #: squirrelbattle/tests/translations_test.py:77 msgid "helmet" -msgstr "" +msgstr "casco" #: squirrelbattle/tests/translations_test.py:78 msgid "chestplate" -msgstr "" +msgstr "pechera" #: squirrelbattle/tests/translations_test.py:79 msgid "shield" -msgstr "" +msgstr "escudo" #: squirrelbattle/tests/translations_test.py:80 msgid "ring of critical damage" -msgstr "" +msgstr "anillo de daño crítico" #: squirrelbattle/tests/translations_test.py:82 msgid "ring of more experience" -msgstr "" +msgstr "anillo de más experiencia" #: squirrelbattle/tests/translations_test.py:84 msgid "monocle" -msgstr "" +msgstr "monóculo" diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index 4657fe8..c8835bd 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -1,3 +1,7 @@ +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} prend {amount} points de dégât." + # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao # This file is distributed under the same license as the squirrelbattle package. @@ -8,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-08 15:15+0100\n" +"POT-Creation-Date: 2021-01-10 20:35+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,110 +21,147 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} prend {amount} points de dégât." - -#: squirrelbattle/display/creditsdisplay.py:28 -#: squirrelbattle/display/menudisplay.py:123 -#: squirrelbattle/display/menudisplay.py:148 -msgid "Credits" -msgstr "" - -#: squirrelbattle/display/creditsdisplay.py:32 -msgid "Developers:" -msgstr "" - -#: squirrelbattle/display/creditsdisplay.py:38 -msgid "Translators:" -msgstr "" - -#: squirrelbattle/display/menudisplay.py:168 -msgid "INVENTORY" -msgstr "INVENTAIRE" - -#: squirrelbattle/display/menudisplay.py:214 -msgid "STALL" -msgstr "STAND" - -#: squirrelbattle/display/statsdisplay.py:44 +#: squirrelbattle/display/gamedisplay.py:150 msgid "Inventory:" msgstr "Inventaire :" -#: squirrelbattle/display/statsdisplay.py:61 +#: squirrelbattle/display/gamedisplay.py:167 msgid "Equipped main:" msgstr "Équipement principal :" -#: squirrelbattle/display/statsdisplay.py:65 +#: squirrelbattle/display/gamedisplay.py:171 msgid "Equipped secondary:" msgstr "Équipement secondaire :" -#: squirrelbattle/display/statsdisplay.py:70 +#: squirrelbattle/display/gamedisplay.py:176 msgid "Equipped chestplate:" msgstr "Plastron équipé :" -#: squirrelbattle/display/statsdisplay.py:74 +#: squirrelbattle/display/gamedisplay.py:180 msgid "Equipped helmet:" msgstr "Casque équipé :" -#: squirrelbattle/display/statsdisplay.py:81 +#: squirrelbattle/display/gamedisplay.py:187 msgid "YOU ARE DEAD" msgstr "VOUS ÊTES MORT" -#: squirrelbattle/display/statsdisplay.py:85 +#: squirrelbattle/display/gamedisplay.py:191 #, python-brace-format msgid "Use {key} to use the ladder" msgstr "Appuyez sur {key} pour utiliser l'échelle" -#: squirrelbattle/display/statsdisplay.py:94 +#: squirrelbattle/display/gamedisplay.py:210 msgid "Move to the friendly entity to talk to it" msgstr "Avancez vers l'entité pour lui parler" -#: squirrelbattle/display/statsdisplay.py:96 +#: squirrelbattle/display/gamedisplay.py:212 #, python-brace-format msgid "Use {key} then move to talk to the entity" msgstr "Appuyez sur {key} puis déplacez-vous pour parler" +#: squirrelbattle/display/menudisplay.py:124 +#: squirrelbattle/display/menudisplay.py:149 +#: squirrelbattle/display/menudisplay.py:304 +msgid "Credits" +msgstr "Crédits" + +#: squirrelbattle/display/menudisplay.py:173 +msgid "INVENTORY" +msgstr "INVENTAIRE" + +#: squirrelbattle/display/menudisplay.py:219 +msgid "STALL" +msgstr "STAND" + +#: squirrelbattle/display/menudisplay.py:263 +msgid "CHEST" +msgstr "COFFRE" + +#: squirrelbattle/display/menudisplay.py:308 +msgid "Developers:" +msgstr "Développeurs:" + +#: squirrelbattle/display/menudisplay.py:314 +msgid "Translators:" +msgstr "Traducteurs:" + #. TODO -#: squirrelbattle/entities/friendly.py:33 +#: squirrelbattle/entities/friendly.py:38 msgid "I don't sell any squirrel" msgstr "Je ne vends pas d'écureuil" -#: squirrelbattle/entities/friendly.py:55 +#: squirrelbattle/entities/friendly.py:68 +msgid "You have opened the chest" +msgstr "Vous avez ouvert le coffre" + +#: squirrelbattle/entities/friendly.py:77 +msgid "The chest exploded" +msgstr "Le coffre a explosé" + +#: squirrelbattle/entities/friendly.py:78 +msgid "It's not really effective" +msgstr "Ce n'est pas très efficace" + +#: squirrelbattle/entities/friendly.py:101 msgid "Flower power!!" msgstr "Pouvoir des fleurs !!" -#: squirrelbattle/entities/friendly.py:55 +#: squirrelbattle/entities/friendly.py:101 msgid "The sun is warm today" msgstr "Le soleil est chaud aujourd'hui" #. The bomb is exploding. #. Each entity that is close to the bomb takes damages. #. The player earn XP if the entity was killed. -#: squirrelbattle/entities/items.py:178 +#: squirrelbattle/entities/items.py:189 msgid "Bomb is exploding." msgstr "La bombe explose." -#: squirrelbattle/entities/items.py:365 +#: squirrelbattle/entities/items.py:385 #, python-brace-format msgid "{player} exchanged its body with {entity}." msgstr "{player} a échangé son corps avec {entity}." -#: squirrelbattle/game.py:200 +#: squirrelbattle/entities/items.py:519 +msgid "" +"The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 " +"turns" +msgstr "" +"TLes ennemis ont -{max(1, self.held_by.intelligence // 2)} de force pour 3 " +"tours" + +#: squirrelbattle/entities/items.py:552 +#, python-brace-format +msgid "{name}" +msgstr "{name}" + +#: squirrelbattle/entities/player.py:83 +msgid "It worked! Nearby ennemies will be confused for 3 turns." +msgstr "Ça a marché ! Les ennemis proches seront confus pendant 3 tours" + +#: squirrelbattle/entities/player.py:86 +msgid "It worked, but there is no one nearby..." +msgstr "Ça a marché, mais il n'y a personne à proximité..." + +#: squirrelbattle/entities/player.py:89 +msgid "The dance was not effective..." +msgstr "La dance n'a pas fonctionné..." + +#: squirrelbattle/game.py:214 #, python-brace-format msgid "The player climbs down to the floor {floor}." msgstr "Le joueur descend à l'étage {floor}." -#: squirrelbattle/game.py:213 +#: squirrelbattle/game.py:227 #, python-brace-format msgid "The player climbs up the floor {floor}." msgstr "Le joueur monte à l'étage {floor}." -#: squirrelbattle/game.py:304 squirrelbattle/tests/game_test.py:603 +#: squirrelbattle/game.py:348 squirrelbattle/tests/game_test.py:631 msgid "The buyer does not have enough money" msgstr "L'acheteur n'a pas assez d'argent" -#: squirrelbattle/game.py:349 +#: squirrelbattle/game.py:423 msgid "" "Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted." @@ -128,7 +169,7 @@ msgstr "" "Certaines clés de votre ficher de sauvegarde sont manquantes.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/game.py:357 +#: squirrelbattle/game.py:431 msgid "" "No player was found on this map!\n" "Maybe you died?" @@ -136,7 +177,7 @@ msgstr "" "Aucun joueur n'a été trouvé sur la carte !\n" "Peut-être êtes-vous mort ?" -#: squirrelbattle/game.py:379 +#: squirrelbattle/game.py:454 msgid "" "The JSON file is not correct.\n" "Your save seems corrupted. It got deleted." @@ -144,26 +185,31 @@ msgstr "" "Le fichier JSON de sauvegarde est incorrect.\n" "Votre sauvegarde semble corrompue. Elle a été supprimée." -#: squirrelbattle/interfaces.py:718 +#: squirrelbattle/interfaces.py:758 squirrelbattle/tests/game_test.py:264 +#, python-brace-format +msgid "{name} is confused, it can not hit {opponent}." +msgstr "{name} est confus et ne peut pas frapper {opponent}." + +#: squirrelbattle/interfaces.py:766 msgid "It's a critical hit!" msgstr "C'est un coup critique !" -#: squirrelbattle/interfaces.py:719 +#: squirrelbattle/interfaces.py:767 #, python-brace-format msgid "{name} hits {opponent}." msgstr "{name} frappe {opponent}." -#: squirrelbattle/interfaces.py:733 +#: squirrelbattle/interfaces.py:781 #, python-brace-format msgid "{name} takes {damage} damage." msgstr "{name} prend {damage} dégâts." -#: squirrelbattle/interfaces.py:735 +#: squirrelbattle/interfaces.py:783 #, python-brace-format msgid "{name} dies." msgstr "{name} meurt." -#: squirrelbattle/interfaces.py:769 +#: squirrelbattle/interfaces.py:817 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} a dit : {message}" @@ -172,8 +218,8 @@ msgstr "{entity} a dit : {message}" msgid "Back" msgstr "Retour" -#: squirrelbattle/tests/game_test.py:368 squirrelbattle/tests/game_test.py:371 -#: squirrelbattle/tests/game_test.py:374 squirrelbattle/tests/game_test.py:377 +#: squirrelbattle/tests/game_test.py:395 squirrelbattle/tests/game_test.py:398 +#: squirrelbattle/tests/game_test.py:401 squirrelbattle/tests/game_test.py:404 #: squirrelbattle/tests/translations_test.py:16 msgid "New game" msgstr "Nouvelle partie" From 893a23f95ccbbb617a012a8e7356ddead48a6fd5 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 21:15:42 +0100 Subject: [PATCH 47/52] Repaired a but : bears dealt 1 damage instead of 0. --- squirrelbattle/interfaces.py | 3 +- .../locale/de/LC_MESSAGES/squirrelbattle.po | 30 +++++++++++-------- .../locale/es/LC_MESSAGES/squirrelbattle.po | 22 +++++++------- .../locale/fr/LC_MESSAGES/squirrelbattle.po | 8 ++--- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 330301c..c71d7c8 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -774,7 +774,8 @@ class FightingEntity(Entity): The entity takes damage from the attacker based on their respective stats. """ - damage = max(1, amount - self.constitution) + if amount != 0 : + damage = max(1, amount - self.constitution) self.health -= damage if self.health <= 0: self.die() diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index 1aed270..a468076 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -1,12 +1,12 @@ -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} nimmt {amount} Schadenspunkte." +msgid "ring_of_critical_damage" +msgstr "" msgid "ring_of_more_experience" msgstr "" -msgid "ring_of_critical_damage" -msgstr "" +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} nimmt {amount} Schadenspunkte." # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 20:35+0100\n" +"POT-Creation-Date: 2021-01-10 21:14+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -63,7 +63,8 @@ msgstr "Ziehen Sie zu der freundlichen Einheit hin, um mit ihr zu sprechen" #: squirrelbattle/display/gamedisplay.py:212 #, python-brace-format msgid "Use {key} then move to talk to the entity" -msgstr "Verwenden Sie {key} dann bewegen Sie sich, um mit der Einheit zu sprechen" +msgstr "" +"Verwenden Sie {key} dann bewegen Sie sich, um mit der Einheit zu sprechen" #: squirrelbattle/display/menudisplay.py:124 #: squirrelbattle/display/menudisplay.py:149 @@ -132,8 +133,9 @@ msgstr "{player} täuscht seinem Körper mit {entity} aus." msgid "" "The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 " "turns" -msgstr "Die Feinde haben 3 Runden lang - {max(1, self.held_by.intelligence // 2)} Stärke" -"" +msgstr "" +"Die Feinde haben 3 Runden lang - {max(1, self.held_by.intelligence // 2)} " +"Stärke" #: squirrelbattle/entities/items.py:552 #, python-brace-format @@ -142,7 +144,9 @@ msgstr "{name}" #: squirrelbattle/entities/player.py:83 msgid "It worked! Nearby ennemies will be confused for 3 turns." -msgstr "Es funktionierte! In der Nähe befindliche Feinde werden 3 Runden lang verwirrt." +msgstr "" +"Es funktionierte! In der Nähe befindliche Feinde werden 3 Runden lang " +"verwirrt." #: squirrelbattle/entities/player.py:86 msgid "It worked, but there is no one nearby..." @@ -204,17 +208,17 @@ msgstr "Es ist ein kritischer Treffer!" msgid "{name} hits {opponent}." msgstr "{name} schlägt {opponent}." -#: squirrelbattle/interfaces.py:781 +#: squirrelbattle/interfaces.py:782 #, python-brace-format msgid "{name} takes {damage} damage." msgstr "{name} erleidet {damage} Schaden." -#: squirrelbattle/interfaces.py:783 +#: squirrelbattle/interfaces.py:784 #, python-brace-format msgid "{name} dies." msgstr "{name} stirbt." -#: squirrelbattle/interfaces.py:817 +#: squirrelbattle/interfaces.py:818 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} hat gesagt: {message}" diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index 7b19c4e..7428cd8 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -1,12 +1,12 @@ -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} recibe {amount} daño." +msgid "ring_of_critical_damage" +msgstr "ring_of_critical_damage" msgid "ring_of_more_experience" msgstr "ring_of_more_experience" -msgid "ring_of_critical_damage" -msgstr "ring_of_critical_damage" +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} recibe {amount} daño." # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 20:35+0100\n" +"POT-Creation-Date: 2021-01-10 21:14+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -132,8 +132,8 @@ msgid "" "The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 " "turns" msgstr "" -"Los enemigos tienen - {max(1, self.held_by.intelligence // 2)} fuerza durante 3" -"turnos" +"Los enemigos tienen - {max(1, self.held_by.intelligence // 2)} fuerza " +"durante 3turnos" #: squirrelbattle/entities/items.py:552 #, python-brace-format @@ -204,17 +204,17 @@ msgstr "¡Es un golpe crítico!" msgid "{name} hits {opponent}." msgstr "{name} golpea a {opponent}." -#: squirrelbattle/interfaces.py:781 +#: squirrelbattle/interfaces.py:782 #, python-brace-format msgid "{name} takes {damage} damage." msgstr "{name} recibe {damage} daño." -#: squirrelbattle/interfaces.py:783 +#: squirrelbattle/interfaces.py:784 #, python-brace-format msgid "{name} dies." msgstr "{name} se muere." -#: squirrelbattle/interfaces.py:817 +#: squirrelbattle/interfaces.py:818 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} dijo : {message}" diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index c8835bd..bdd5132 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 20:35+0100\n" +"POT-Creation-Date: 2021-01-10 21:14+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -199,17 +199,17 @@ msgstr "C'est un coup critique !" msgid "{name} hits {opponent}." msgstr "{name} frappe {opponent}." -#: squirrelbattle/interfaces.py:781 +#: squirrelbattle/interfaces.py:782 #, python-brace-format msgid "{name} takes {damage} damage." msgstr "{name} prend {damage} dégâts." -#: squirrelbattle/interfaces.py:783 +#: squirrelbattle/interfaces.py:784 #, python-brace-format msgid "{name} dies." msgstr "{name} meurt." -#: squirrelbattle/interfaces.py:817 +#: squirrelbattle/interfaces.py:818 #, python-brace-format msgid "{entity} said: {message}" msgstr "{entity} a dit : {message}" From 4f4223c514acb6fde43ce3277a66cefcb1b3dd6c Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 21:25:15 +0100 Subject: [PATCH 48/52] More translations. --- .../locale/de/LC_MESSAGES/squirrelbattle.po | 64 +++++++++++-------- .../locale/es/LC_MESSAGES/squirrelbattle.po | 60 +++++++++-------- .../locale/fr/LC_MESSAGES/squirrelbattle.po | 52 ++++++++------- squirrelbattle/tests/translations_test.py | 4 ++ 4 files changed, 104 insertions(+), 76 deletions(-) diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index a468076..3d9595a 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -1,12 +1,12 @@ -msgid "ring_of_critical_damage" -msgstr "" +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} nimmt {amount} Schadenspunkte." msgid "ring_of_more_experience" msgstr "" -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} nimmt {amount} Schadenspunkte." +msgid "ring_of_critical_damage" +msgstr "" # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 21:14+0100\n" +"POT-Creation-Date: 2021-01-10 21:20+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -103,7 +103,7 @@ msgstr "Sie haben der Kaste geöffnet" #: squirrelbattle/entities/friendly.py:77 msgid "The chest exploded" -msgstr "" +msgstr "Der Kaste explodierte" #: squirrelbattle/entities/friendly.py:78 msgid "It's not really effective" @@ -318,85 +318,93 @@ msgid "Key used to use ladders" msgstr "Leitertaste" #: squirrelbattle/tests/translations_test.py:58 +msgid "Key used to use a bow" +msgstr "Bogentaste" + +#: squirrelbattle/tests/translations_test.py:60 +msgid "Key used to dance" +msgstr "Tanztaste" + +#: squirrelbattle/tests/translations_test.py:62 msgid "Texture pack" msgstr "Textur-Packung" -#: squirrelbattle/tests/translations_test.py:59 +#: squirrelbattle/tests/translations_test.py:63 msgid "Language" msgstr "Sprache" -#: squirrelbattle/tests/translations_test.py:62 +#: squirrelbattle/tests/translations_test.py:66 msgid "player" msgstr "Spieler" -#: squirrelbattle/tests/translations_test.py:64 +#: squirrelbattle/tests/translations_test.py:68 msgid "hedgehog" msgstr "Igel" -#: squirrelbattle/tests/translations_test.py:65 +#: squirrelbattle/tests/translations_test.py:69 msgid "merchant" msgstr "Kaufmann" -#: squirrelbattle/tests/translations_test.py:66 +#: squirrelbattle/tests/translations_test.py:70 msgid "rabbit" msgstr "Kanninchen" -#: squirrelbattle/tests/translations_test.py:67 +#: squirrelbattle/tests/translations_test.py:71 msgid "sunflower" msgstr "Sonnenblume" -#: squirrelbattle/tests/translations_test.py:68 +#: squirrelbattle/tests/translations_test.py:72 msgid "teddy bear" msgstr "Teddybär" -#: squirrelbattle/tests/translations_test.py:69 +#: squirrelbattle/tests/translations_test.py:73 msgid "tiger" msgstr "Tiger" -#: squirrelbattle/tests/translations_test.py:70 +#: squirrelbattle/tests/translations_test.py:74 msgid "eagle" msgstr "Adler" -#: squirrelbattle/tests/translations_test.py:72 +#: squirrelbattle/tests/translations_test.py:76 msgid "body snatch potion" msgstr "Leichenfleddererzaubertrank" -#: squirrelbattle/tests/translations_test.py:73 +#: squirrelbattle/tests/translations_test.py:77 msgid "bomb" msgstr "Bombe" -#: squirrelbattle/tests/translations_test.py:74 +#: squirrelbattle/tests/translations_test.py:78 msgid "explosion" msgstr "Explosion" -#: squirrelbattle/tests/translations_test.py:75 +#: squirrelbattle/tests/translations_test.py:79 msgid "heart" msgstr "Herz" -#: squirrelbattle/tests/translations_test.py:76 +#: squirrelbattle/tests/translations_test.py:80 msgid "sword" msgstr "schwert" -#: squirrelbattle/tests/translations_test.py:77 +#: squirrelbattle/tests/translations_test.py:81 msgid "helmet" -msgstr "" +msgstr "Helm" -#: squirrelbattle/tests/translations_test.py:78 +#: squirrelbattle/tests/translations_test.py:82 msgid "chestplate" msgstr "Brustpanzer" -#: squirrelbattle/tests/translations_test.py:79 +#: squirrelbattle/tests/translations_test.py:83 msgid "shield" msgstr "Schild" -#: squirrelbattle/tests/translations_test.py:80 +#: squirrelbattle/tests/translations_test.py:84 msgid "ring of critical damage" msgstr "Ring des kritischen Schadens" -#: squirrelbattle/tests/translations_test.py:82 +#: squirrelbattle/tests/translations_test.py:86 msgid "ring of more experience" msgstr "Ring der mehr Erfahrung" -#: squirrelbattle/tests/translations_test.py:84 +#: squirrelbattle/tests/translations_test.py:88 msgid "monocle" msgstr "Monokel" diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index 7428cd8..e77f059 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -1,12 +1,12 @@ -msgid "ring_of_critical_damage" -msgstr "ring_of_critical_damage" +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} recibe {amount} daño." msgid "ring_of_more_experience" msgstr "ring_of_more_experience" -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} recibe {amount} daño." +msgid "ring_of_critical_damage" +msgstr "ring_of_critical_damage" # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 21:14+0100\n" +"POT-Creation-Date: 2021-01-10 21:20+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -314,85 +314,93 @@ msgid "Key used to use ladders" msgstr "Tecla para el uso de las escaleras" #: squirrelbattle/tests/translations_test.py:58 +msgid "Key used to use a bow" +msgstr "Tecla para usar un arco" + +#: squirrelbattle/tests/translations_test.py:60 +msgid "Key used to dance" +msgstr "Tecla para bailar" + +#: squirrelbattle/tests/translations_test.py:62 msgid "Texture pack" msgstr "Paquete de texturas" -#: squirrelbattle/tests/translations_test.py:59 +#: squirrelbattle/tests/translations_test.py:63 msgid "Language" msgstr "Languaje" -#: squirrelbattle/tests/translations_test.py:62 +#: squirrelbattle/tests/translations_test.py:66 msgid "player" msgstr "jugador" -#: squirrelbattle/tests/translations_test.py:64 +#: squirrelbattle/tests/translations_test.py:68 msgid "hedgehog" msgstr "erizo" -#: squirrelbattle/tests/translations_test.py:65 +#: squirrelbattle/tests/translations_test.py:69 msgid "merchant" msgstr "comerciante" -#: squirrelbattle/tests/translations_test.py:66 +#: squirrelbattle/tests/translations_test.py:70 msgid "rabbit" msgstr "conejo" -#: squirrelbattle/tests/translations_test.py:67 +#: squirrelbattle/tests/translations_test.py:71 msgid "sunflower" msgstr "girasol" -#: squirrelbattle/tests/translations_test.py:68 +#: squirrelbattle/tests/translations_test.py:72 msgid "teddy bear" msgstr "osito de peluche" -#: squirrelbattle/tests/translations_test.py:69 +#: squirrelbattle/tests/translations_test.py:73 msgid "tiger" msgstr "tigre" -#: squirrelbattle/tests/translations_test.py:70 +#: squirrelbattle/tests/translations_test.py:74 msgid "eagle" msgstr "águila" -#: squirrelbattle/tests/translations_test.py:72 +#: squirrelbattle/tests/translations_test.py:76 msgid "body snatch potion" msgstr "poción de intercambio" -#: squirrelbattle/tests/translations_test.py:73 +#: squirrelbattle/tests/translations_test.py:77 msgid "bomb" msgstr "bomba" -#: squirrelbattle/tests/translations_test.py:74 +#: squirrelbattle/tests/translations_test.py:78 msgid "explosion" msgstr "explosión" -#: squirrelbattle/tests/translations_test.py:75 +#: squirrelbattle/tests/translations_test.py:79 msgid "heart" msgstr "corazón" -#: squirrelbattle/tests/translations_test.py:76 +#: squirrelbattle/tests/translations_test.py:80 msgid "sword" msgstr "espada" -#: squirrelbattle/tests/translations_test.py:77 +#: squirrelbattle/tests/translations_test.py:81 msgid "helmet" msgstr "casco" -#: squirrelbattle/tests/translations_test.py:78 +#: squirrelbattle/tests/translations_test.py:82 msgid "chestplate" msgstr "pechera" -#: squirrelbattle/tests/translations_test.py:79 +#: squirrelbattle/tests/translations_test.py:83 msgid "shield" msgstr "escudo" -#: squirrelbattle/tests/translations_test.py:80 +#: squirrelbattle/tests/translations_test.py:84 msgid "ring of critical damage" msgstr "anillo de daño crítico" -#: squirrelbattle/tests/translations_test.py:82 +#: squirrelbattle/tests/translations_test.py:86 msgid "ring of more experience" msgstr "anillo de más experiencia" -#: squirrelbattle/tests/translations_test.py:84 +#: squirrelbattle/tests/translations_test.py:88 msgid "monocle" msgstr "monóculo" diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index bdd5132..91c1731 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 21:14+0100\n" +"POT-Creation-Date: 2021-01-10 21:20+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -127,7 +127,7 @@ msgid "" "The ennemies have -{max(1, self.held_by.intelligence // 2)}strength for 3 " "turns" msgstr "" -"TLes ennemis ont -{max(1, self.held_by.intelligence // 2)} de force pour 3 " +"Les ennemis ont -{max(1, self.held_by.intelligence // 2)} de force pour 3 " "tours" #: squirrelbattle/entities/items.py:552 @@ -309,85 +309,93 @@ msgid "Key used to use ladders" msgstr "Touche pour utiliser les échelles" #: squirrelbattle/tests/translations_test.py:58 +msgid "Key used to use a bow" +msgstr "Touche pour utiliser un arc" + +#: squirrelbattle/tests/translations_test.py:60 +msgid "Key used to dance" +msgstr "Touche pour danser" + +#: squirrelbattle/tests/translations_test.py:62 msgid "Texture pack" msgstr "Pack de textures" -#: squirrelbattle/tests/translations_test.py:59 +#: squirrelbattle/tests/translations_test.py:63 msgid "Language" msgstr "Langue" -#: squirrelbattle/tests/translations_test.py:62 +#: squirrelbattle/tests/translations_test.py:66 msgid "player" msgstr "joueur" -#: squirrelbattle/tests/translations_test.py:64 +#: squirrelbattle/tests/translations_test.py:68 msgid "hedgehog" msgstr "hérisson" -#: squirrelbattle/tests/translations_test.py:65 +#: squirrelbattle/tests/translations_test.py:69 msgid "merchant" msgstr "marchand" -#: squirrelbattle/tests/translations_test.py:66 +#: squirrelbattle/tests/translations_test.py:70 msgid "rabbit" msgstr "lapin" -#: squirrelbattle/tests/translations_test.py:67 +#: squirrelbattle/tests/translations_test.py:71 msgid "sunflower" msgstr "tournesol" -#: squirrelbattle/tests/translations_test.py:68 +#: squirrelbattle/tests/translations_test.py:72 msgid "teddy bear" msgstr "nounours" -#: squirrelbattle/tests/translations_test.py:69 +#: squirrelbattle/tests/translations_test.py:73 msgid "tiger" msgstr "tigre" -#: squirrelbattle/tests/translations_test.py:70 +#: squirrelbattle/tests/translations_test.py:74 msgid "eagle" msgstr "pygargue" -#: squirrelbattle/tests/translations_test.py:72 +#: squirrelbattle/tests/translations_test.py:76 msgid "body snatch potion" msgstr "potion d'arrachage de corps" -#: squirrelbattle/tests/translations_test.py:73 +#: squirrelbattle/tests/translations_test.py:77 msgid "bomb" msgstr "bombe" -#: squirrelbattle/tests/translations_test.py:74 +#: squirrelbattle/tests/translations_test.py:78 msgid "explosion" msgstr "explosion" -#: squirrelbattle/tests/translations_test.py:75 +#: squirrelbattle/tests/translations_test.py:79 msgid "heart" msgstr "cœur" -#: squirrelbattle/tests/translations_test.py:76 +#: squirrelbattle/tests/translations_test.py:80 msgid "sword" msgstr "épée" -#: squirrelbattle/tests/translations_test.py:77 +#: squirrelbattle/tests/translations_test.py:81 msgid "helmet" msgstr "casque" -#: squirrelbattle/tests/translations_test.py:78 +#: squirrelbattle/tests/translations_test.py:82 msgid "chestplate" msgstr "plastron" -#: squirrelbattle/tests/translations_test.py:79 +#: squirrelbattle/tests/translations_test.py:83 msgid "shield" msgstr "bouclier" -#: squirrelbattle/tests/translations_test.py:80 +#: squirrelbattle/tests/translations_test.py:84 msgid "ring of critical damage" msgstr "anneau de coup critique" -#: squirrelbattle/tests/translations_test.py:82 +#: squirrelbattle/tests/translations_test.py:86 msgid "ring of more experience" msgstr "anneau de plus d'expérience" -#: squirrelbattle/tests/translations_test.py:84 +#: squirrelbattle/tests/translations_test.py:88 msgid "monocle" msgstr "monocle" diff --git a/squirrelbattle/tests/translations_test.py b/squirrelbattle/tests/translations_test.py index bdf0238..c0ad3c0 100644 --- a/squirrelbattle/tests/translations_test.py +++ b/squirrelbattle/tests/translations_test.py @@ -55,6 +55,10 @@ class TestTranslations(unittest.TestCase): self.assertEqual(_("Key used to wait"), "Touche pour attendre") self.assertEqual(_("Key used to use ladders"), "Touche pour utiliser les échelles") + self.assertEqual(_("Key used to use a bow"), + "Touche pour utiliser un arc") + self.assertEqual(_("Key used to dance"), + "Touche pour danser") self.assertEqual(_("Texture pack"), "Pack de textures") self.assertEqual(_("Language"), "Langue") From 6cf0590586e034a864c146a9503c72b248135f62 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 21:38:21 +0100 Subject: [PATCH 49/52] even more translations. --- squirrelbattle/entities/items.py | 4 +- .../locale/de/LC_MESSAGES/squirrelbattle.po | 44 ++++++++++++++---- .../locale/es/LC_MESSAGES/squirrelbattle.po | 46 +++++++++++++++---- .../locale/fr/LC_MESSAGES/squirrelbattle.po | 34 ++++++++++++-- squirrelbattle/tests/translations_test.py | 5 ++ 5 files changed, 111 insertions(+), 22 deletions(-) diff --git a/squirrelbattle/entities/items.py b/squirrelbattle/entities/items.py index ac5f74d..f4587f3 100644 --- a/squirrelbattle/entities/items.py +++ b/squirrelbattle/entities/items.py @@ -597,7 +597,7 @@ class Bow(LongRangeWeapon): @property def string(self) -> str: - return " is shot by an arrow." + return _(" is shot by an arrow.") class FireBallStaff(LongRangeWeapon): @@ -619,7 +619,7 @@ class FireBallStaff(LongRangeWeapon): @property def string(self) -> str: - return " is shot by a fire ball." + return _(" is shot by a fire ball.") def throw(self, direction: int) -> Any: """ diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index 3d9595a..f4e3cf8 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -1,12 +1,12 @@ -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} nimmt {amount} Schadenspunkte." +msgid "ring_of_critical_damage" +msgstr "" msgid "ring_of_more_experience" msgstr "" -msgid "ring_of_critical_damage" -msgstr "" +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} nimmt {amount} Schadenspunkte." # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 21:20+0100\n" +"POT-Creation-Date: 2021-01-10 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -142,6 +142,14 @@ msgstr "" msgid "{name}" msgstr "{name}" +#: squirrelbattle/entities/items.py:600 +msgid " is shot by an arrow." +msgstr " wird von einem Pfeil erschossen." + +#: squirrelbattle/entities/items.py:622 +msgid " is shot by a fire ball." +msgstr " wird von eine Feuerball erschossen." + #: squirrelbattle/entities/player.py:83 msgid "It worked! Nearby ennemies will be confused for 3 turns." msgstr "" @@ -398,13 +406,33 @@ msgid "shield" msgstr "Schild" #: squirrelbattle/tests/translations_test.py:84 +msgid "ruler" +msgstr "Lineal" + +#: squirrelbattle/tests/translations_test.py:85 +msgid "scroll of damage" +msgstr "Schriftrolle des Schadens" + +#: squirrelbattle/tests/translations_test.py:86 +msgid "scroll of weakness" +msgstr "Schriftrolle der Schwäche" + +#: squirrelbattle/tests/translations_test.py:87 +msgid "bow" +msgstr "Bogen" + +#: squirrelbattle/tests/translations_test.py:88 +msgid "fire ball staff" +msgstr "Feuerball Stab" + +#: squirrelbattle/tests/translations_test.py:89 msgid "ring of critical damage" msgstr "Ring des kritischen Schadens" -#: squirrelbattle/tests/translations_test.py:86 +#: squirrelbattle/tests/translations_test.py:91 msgid "ring of more experience" msgstr "Ring der mehr Erfahrung" -#: squirrelbattle/tests/translations_test.py:88 +#: squirrelbattle/tests/translations_test.py:93 msgid "monocle" msgstr "Monokel" diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index e77f059..2d8ee64 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -1,12 +1,12 @@ -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} recibe {amount} daño." +msgid "ring_of_critical_damage" +msgstr "ring_of_critical_damage" msgid "ring_of_more_experience" msgstr "ring_of_more_experience" -msgid "ring_of_critical_damage" -msgstr "ring_of_critical_damage" +#, python-brace-format +msgid "{name} takes {amount} damage." +msgstr "{name} recibe {amount} daño." # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 21:20+0100\n" +"POT-Creation-Date: 2021-01-10 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -69,7 +69,7 @@ msgstr "Usa {key} y luego muévete para hablar con la entidad" #: squirrelbattle/display/menudisplay.py:149 #: squirrelbattle/display/menudisplay.py:304 msgid "Credits" -msgstr "" +msgstr "Creditos" #: squirrelbattle/display/menudisplay.py:173 msgid "INVENTORY" @@ -140,6 +140,14 @@ msgstr "" msgid "{name}" msgstr "{name}" +#: squirrelbattle/entities/items.py:600 +msgid " is shot by an arrow." +msgstr " es disparado por una flecha." + +#: squirrelbattle/entities/items.py:622 +msgid " is shot by a fire ball." +msgstr " es disparado por una bola de fuego." + #: squirrelbattle/entities/player.py:83 msgid "It worked! Nearby ennemies will be confused for 3 turns." msgstr "¡Funcionó! Los enemigos cercanos se confundirán durante 3 turnos." @@ -394,13 +402,33 @@ msgid "shield" msgstr "escudo" #: squirrelbattle/tests/translations_test.py:84 +msgid "ruler" +msgstr "Regla" + +#: squirrelbattle/tests/translations_test.py:85 +msgid "scroll of damage" +msgstr "rollo de daño" + +#: squirrelbattle/tests/translations_test.py:86 +msgid "scroll of weakness" +msgstr "rollo de debilidad" + +#: squirrelbattle/tests/translations_test.py:87 +msgid "bow" +msgstr "arco" + +#: squirrelbattle/tests/translations_test.py:88 +msgid "fire ball staff" +msgstr "bastón de bola de fuego" + +#: squirrelbattle/tests/translations_test.py:89 msgid "ring of critical damage" msgstr "anillo de daño crítico" -#: squirrelbattle/tests/translations_test.py:86 +#: squirrelbattle/tests/translations_test.py:91 msgid "ring of more experience" msgstr "anillo de más experiencia" -#: squirrelbattle/tests/translations_test.py:88 +#: squirrelbattle/tests/translations_test.py:93 msgid "monocle" msgstr "monóculo" diff --git a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po index 91c1731..93b524d 100644 --- a/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/fr/LC_MESSAGES/squirrelbattle.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: squirrelbattle 3.14.1\n" "Report-Msgid-Bugs-To: squirrel-battle@crans.org\n" -"POT-Creation-Date: 2021-01-10 21:20+0100\n" +"POT-Creation-Date: 2021-01-10 21:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -135,6 +135,14 @@ msgstr "" msgid "{name}" msgstr "{name}" +#: squirrelbattle/entities/items.py:600 +msgid " is shot by an arrow." +msgstr " est frappé par une flèche." + +#: squirrelbattle/entities/items.py:622 +msgid " is shot by a fire ball." +msgstr " est frappé par une boule de feu." + #: squirrelbattle/entities/player.py:83 msgid "It worked! Nearby ennemies will be confused for 3 turns." msgstr "Ça a marché ! Les ennemis proches seront confus pendant 3 tours" @@ -389,13 +397,33 @@ msgid "shield" msgstr "bouclier" #: squirrelbattle/tests/translations_test.py:84 +msgid "ruler" +msgstr "règle" + +#: squirrelbattle/tests/translations_test.py:85 +msgid "scroll of damage" +msgstr "parchemin de dégâts" + +#: squirrelbattle/tests/translations_test.py:86 +msgid "scroll of weakness" +msgstr "parchemin de faiblesse" + +#: squirrelbattle/tests/translations_test.py:87 +msgid "bow" +msgstr "arc" + +#: squirrelbattle/tests/translations_test.py:88 +msgid "fire ball staff" +msgstr "baton de boule de feu" + +#: squirrelbattle/tests/translations_test.py:89 msgid "ring of critical damage" msgstr "anneau de coup critique" -#: squirrelbattle/tests/translations_test.py:86 +#: squirrelbattle/tests/translations_test.py:91 msgid "ring of more experience" msgstr "anneau de plus d'expérience" -#: squirrelbattle/tests/translations_test.py:88 +#: squirrelbattle/tests/translations_test.py:93 msgid "monocle" msgstr "monocle" diff --git a/squirrelbattle/tests/translations_test.py b/squirrelbattle/tests/translations_test.py index c0ad3c0..2db869a 100644 --- a/squirrelbattle/tests/translations_test.py +++ b/squirrelbattle/tests/translations_test.py @@ -81,6 +81,11 @@ class TestTranslations(unittest.TestCase): self.assertEqual(_("helmet"), "casque") self.assertEqual(_("chestplate"), "plastron") self.assertEqual(_("shield"), "bouclier") + self.assertEqual(_("ruler"), "règle") + self.assertEqual(_("scroll of damage"), "parchemin de dégâts") + self.assertEqual(_("scroll of weakness"), "parchemin de faiblesse") + self.assertEqual(_("bow"), "arc") + self.assertEqual(_("fire ball staff"), "baton de boule de feu") self.assertEqual(_("ring of critical damage"), "anneau de coup critique") self.assertEqual(_("ring of more experience"), From 18ace5144c298add03dffb49b82d3a187566876d Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 21:41:45 +0100 Subject: [PATCH 50/52] Repaired a bug : a variable was not declared before it was used in interfaces.py take_damage --- squirrelbattle/interfaces.py | 1 + 1 file changed, 1 insertion(+) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index c71d7c8..ea59a04 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -774,6 +774,7 @@ class FightingEntity(Entity): The entity takes damage from the attacker based on their respective stats. """ + damage = 0 if amount != 0 : damage = max(1, amount - self.constitution) self.health -= damage From 236481ae1c10de731a1876466568532eba9bfd1c Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Sun, 10 Jan 2021 21:44:45 +0100 Subject: [PATCH 51/52] linting --- squirrelbattle/interfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index ea59a04..306f857 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -775,7 +775,7 @@ class FightingEntity(Entity): based on their respective stats. """ damage = 0 - if amount != 0 : + if amount != 0: damage = max(1, amount - self.constitution) self.health -= damage if self.health <= 0: From d3607248c0020f1978c5733b2b725b4212780581 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 21:48:46 +0100 Subject: [PATCH 52/52] Remove unused translations --- squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po | 10 ---------- squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po index f4e3cf8..74eb1a9 100644 --- a/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/de/LC_MESSAGES/squirrelbattle.po @@ -1,13 +1,3 @@ -msgid "ring_of_critical_damage" -msgstr "" - -msgid "ring_of_more_experience" -msgstr "" - -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} nimmt {amount} Schadenspunkte." - # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao # This file is distributed under the same license as the squirrelbattle package. diff --git a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po index 2d8ee64..909ff48 100644 --- a/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po +++ b/squirrelbattle/locale/es/LC_MESSAGES/squirrelbattle.po @@ -1,13 +1,3 @@ -msgid "ring_of_critical_damage" -msgstr "ring_of_critical_damage" - -msgid "ring_of_more_experience" -msgstr "ring_of_more_experience" - -#, python-brace-format -msgid "{name} takes {amount} damage." -msgstr "{name} recibe {amount} daño." - # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR ÿnérant, eichhornchen, nicomarg, charlse, ifugao # This file is distributed under the same license as the squirrelbattle package.