From f6210a6356cd0a42b992395e84143bc56a62625a Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 8 Jan 2021 18:06:26 +0100 Subject: [PATCH] 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']