Source code for pyzork.equipment

from .enums import *
from .base import QM
from .utils import post_output, get, _getattr

class Item:
    """An item is a physical object the entity can interact with and carry aroun with them everywhere they
    various subclasses of item have various uses. Equipments are meant to provide a buff when equipped, consumables
    provide an effect, kinda like a handheld ability. Quest items don't usually provide an direct benefit but rather
    are a prequisite for unlocking certain areas and progressing the story.
    
    Parameters
    -----------
    name : str
        The name of the item, if it is not provided then defaults first to the class docs and then the class name
    description : Optional[str]
        The description of the item
    
    Attributes
    -----------
    name : str
        Name of the item
    description : str
        Description of the item
    """ 
    def __repr__(self):
        return f"<{self.name}>"
        
    def __str__(self):
        return self.name
        
    def __hash__(self):
        return hash(self.name)
        
    def __eq__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
            
        return self.name == other.name

        
[docs]class Consumable(Item): """Consumable are items which the entity can use at pretty much anytime to gain an effect, kinda like an ability with limited uses which costs no energy. Each consumable can be used a certain number of times before it disappears, if the entity buys more of the consumable then the charges will be added together. When selling a consumable the charges will be split up based on how many charges the buyer accepts, for example if the shop takes consumables with 2 charges and you have one with 5 then it will make two sales and discrd the remainer. Parameters ----------- name : str The name of the item, if it is not provided then defaults first to the class docs and then the class name description : Optional[str] The description of the item charges : int The amount of times this item can be used Attributes ----------- name : str Name of the item description : str Description of the item charges : int The amount of times this item can be used """ def __init__(self, **kwargs): self.name = _getattr(self, "name", kwargs, self.__doc__ if self.__doc__ else self.__class__.__name__) self.charges = _getattr(self, "charges", kwargs) self.description = _getattr(self, "description", kwargs, self.effect.__doc__) def use(self, target): if self.charges > 0: self.charges -= 1 self.effect(target) return True else: return False
[docs] def effect(self, target): """The function called when the item is used, overwrite this to cause an effect Parameters ----------- target : Entity The target of the item, can be the user or could be an NPC """ pass
[docs] @classmethod def add(cls, **kwargs): """Decorator method to create a consumable off a method, takes the same parameters as the class.""" def decorator(func): new_class = type(func.__name__, (cls,), { "charges": kwargs.pop("charges"), "effect": func, "name": kwargs.pop("name", func.__name__), "description": kwargs.pop("description", func.__doc__), }) return new_class return decorator
[docs]class QuestItem(Item): """Quest items are special item which do nothing on their own but can be used as requirement for certain part of the story, for example the guards at the palace entrance won't let you enter unless you have a letter of recommendation in your inventory. Parameters ----------- name : str The name of the item, if it is not provided then defaults first to the class docs and then the class name description : Optional[str] The description of the item Attributes ----------- name : str Name of the item description : str Description of the item """ def __init__(self, **kwargs): self.name = _getattr(self, "name", kwargs, self.__doc__ if self.__doc__ else self.__class__.__name__) self.description = _getattr(self, "description", kwargs, self.__init__.__doc__) @classmethod def from_dict(cls, **kwargs): """Create a QuestItem from a set of kwargs, takes the same parameters as the class and returns a subclass of it by the same name.""" new_class = type(kwargs.pop("name"), (cls,), kwargs) return new_class
class Equipment(Item): """Equipment can't be used on their own and don't do anything special while they sit in your inventory. They're instead meant to be equipped on the entity, in which case they can provide a buff and effect Parameters ----------- name : str The name of the item, if it is not provided then defaults first to the class docs and then the class name description : Optional[str] The description of the item Attributes ----------- name : str Name of the item description : str Description of the item """ def __init__(self, **kwargs): self.name = _getattr(self, "name", kwargs, self.__doc__ if self.__doc__ else self.__class__.__name__) self.description = _getattr(self, "description", kwargs, f"{self.buff.__doc__} {self.effect.__doc__}") def calc(self, entity): return self.buff(entity) def buff(self, entity): """Abstract method that must be implemented by every piece of equipment, this is the method used when calculating damage that dictates the various modifiers and buffs gotten""" return [] def effect(self, target): """Abstract class that applies an effect when attacking""" pass @classmethod def add_effect(cls, **kwargs): """Decorator function to allow the user to define an effect by decorating a function. Takes the same parameters as the class. """ def decorator(func): if not cls in [Weapon, Armor]: cls.effect = func cls.description = f"{cls.description} {func.__doc__}" return func else: new_class = type(func.__name__, (cls,), { "effect": func, "name": kwargs.pop("name", func.__name__), "description": kwargs.pop("description", func.__doc__), }) return new_class return decorator @classmethod def add_buff(cls, **kwargs): """Decorator function to allow the user to define a buff by decorating a function. Takes the same parameters as the class. Since this specifically adds a buff, the `stat_type` parameter is required""" def decorator(func): if not cls in [Weapon, Armor]: cls.buff = func cls.description = f"{func.__doc__} {cls.description}" return func else: new_class = type(func.__name__, (cls,), { "buff": func, "name": kwargs.pop("name", func.__name__), "description": kwargs.pop("description", func.__doc__), }) return new_class return decorator
[docs]class Armor(Equipment): pass
[docs]class Weapon(Equipment): pass
NullWeapon = Weapon(name="bare hands") NullArmor = Armor(name="linen clothes")
[docs]class ShopItem: """A shop item is an item that can be sold or bought in a shop. Parameters ----------- item : Union[Consumable, QuestItem, Equipment] The item to sell, this is the class itself, not an instance of the object price : int The price of the item amount : int The number of items that are available for sale, if a entity sells an item it will increment the amount Attributes ----------- item : Union[Consumable, QuestItem, Equipment] The item being sold or bought price : int The price at which it is being sold amount : int How many more times the item can be sold name : int The name of the item being sold description The description of the item being sold """ def __init__(self, **kwargs): self.item = kwargs.pop("item") self.price = kwargs.pop("price") self.charges = kwargs.pop("amount", 0) self.fake_inst = self.item() def __repr__(self): return f"<{self.fake_inst.name} amount={self.charges} price={self.price}>" @property def name(self): return self.fake_inst.name @property def description(self): return self.fake_inst.description
[docs] def buy(self, entity : "Entity"): """Buy an instance of that item Parameters ----------- entity : Entity The entity purchasing the item """ if not self.charges > 0: return post_output("No items left") if entity.money < self.price: return post_output("Not enough money") self.charges -= 1 entity.remove_money(self.price) entity.inventory.add_item(self.item()) post_output(f"Bought {self.fake_inst.name} and payed {self.price}")
[docs] def sell(self, entity : "Entity", resell : float): """Sell an instance of that item Parameters ----------- entity : Entity The entity selling the item resell : float The resale value, this is usually passed down from the shop instance """ to_remove = entity.inventory.get_item(name=self.fake_inst.name) if to_remove is None: return post_output("Couldn't find the item") if isinstance(to_remove, Consumable): entity.inventory.remove_item(to_remove) amount = to_remove.amount // self.fake_inst.amount self.charges += amount money = (self.price * resell) * amount entity.add_money(money) else: entity.inventory.remove_item(to_remove) self.charges += 1 money = self.price * resell entity.add_money(money) post_output(f"Sold {self.fake_inst.name} and gained {money}")
[docs]class Inventory: """Inventories store all items: Equipment, Consumables and QuestItem. Parameters ------------ items : List[Union[Consumable, Equipment, QuestItem]] A list of items this inventory starts with weapon : Weapon The weapon this inventory comes equipped with by default armor : Armor The armor this inventory comes equipped with by default """ def __init__(self, **kwargs): self.consumables = {} self.quest = [] self.equipment = set() for item in kwargs.get("items", []): self.add_item(item) self.weapon = kwargs.get("weapon", NullWeapon) self.armor = kwargs.get("armor", NullArmor) def __repr__(self): return f"<Inventory consumables={len(self.consumables)} quest={len(self.quest)} equipment={len(self.equipment)}>"
[docs] def print(self): """Print all the item in the entity's inventory""" post_output(f"Consumables: {self.consumables}") post_output(f"Quest Items: {self.quest}") post_output(f"Equipment: {self.equipment}")
[docs] def add_item(self, item : Item): """Add an item to the entity's inventory Parameters ------------ item : Item The item you want to add to the inventory, the method will sort out where it needs to go. """ if isinstance(item, Equipment): self.equipment.add(item) # post_output(f"Equipment {item.name} added") if isinstance(item, Consumable): if type(item) in self.consumables: self.consumables[type(item)].amount += item.amount else: self.consumables[type(item)] = item # post_output(f"Consumable {item.name} added") if isinstance(item, QuestItem): self.quest.append(item) # post_output(f"Quest item {item.name} added") QM.progress_quests("on_pickup", item)
[docs] def use_item(self, item : Consumable, target): """Use a consumable on a target Parameters ----------- item : Consumable The item to use target : Entity The entity to use it on """ used = item.use(target) if item.charges < 1: del self.consumables[type(item)] if not used: post_output("You cannot use this item") return used
[docs] def equip_item(self, item : Equipment): """Equip either a weapon or armor Parameters ----------- item : Equipment The piece of equipment to add """ if not isinstance(item, Equipment): raise TypeError("This type of item cannot be equipped") if isinstance(item, Weapon): self.equipment.add(self.weapon) self.weapon = item self.equipment.remove(self.weapon) post_output(f"Weapon {item.name} equipped") else: self.equipment.add(self.armor) self.armor = item self.equipment.remove(self.armor) post_output(f"Armor {item.name} equipped")
[docs] def remove_item(self, item : Item): """Remove an item from the inventory Parameters ----------- item : Item The item to remove """ if isinstance(item, Equipment): self.equipment.remove(item) post_output(f"Equipment {item.name} removed") elif isinstance(item, Consumable): del self.consumables[type(item)] post_output(f"Consumable {item.name} removed") elif isinstance(item, QuestItem): post_output(f"Quest Item {item.name} removed") self.quest.remove(item)
[docs] def print_consumables(self): """Print all the consumables""" for consumable in self.consumables: post_output(f"- {consumable.name}: {consumable.description}")
[docs] def print_equipment(self): """Print all the equipments""" for equipment in self.equipment: post_output(f"- {equipment.name}: {equipment.description}")
[docs] def print_quest(self): """Print all the quest items""" for quest in self.quest: post_output(f"- {quest.name}: {quest.description}")
[docs] def get_equipment(self, **kwargs): """Get an equipment instance based on a parameter. The parameter is any kwarg to you pass, if you want the parameter to be the name of the item then you can do something like `get_equipment(name='Big Sword')`""" return get(self.equipment, **kwargs)
[docs] def get_quest(self, **kwargs): """Get an quest instance based on a parameter. The parameter is any kwarg to you pass, if you want the parameter to be the name of the item then you can do something like `get_quest(name='Old Key')`""" return get(self.quest, **kwargs)
[docs] def get_consumable(self, **kwargs): """Get an consumable instance based on a parameter. The parameter is any kwarg to you pass, if you want the parameter to be the name of the item then you can do something like `get_consumable(name='Health Potion')`""" return get(self.consumables.values(), **kwargs)
[docs] def get_item(self, **kwargs): """Get an equipment, consumable or quest instance based on a parameter. The parameter is any kwarg to you pass, if you want the parameter to be the name of the item then you can do something like `get_item(name='Big Sword')`""" item = self.get_consumable(**kwargs) if item is not None: return item item = self.get_quest(**kwargs) if item is not None: return item item = self.get_equipment(**kwargs) return item