Skip to content
Snippets Groups Projects
items.py 15 KiB
Newer Older
class GameObject:
    '''
    This is a base class for game objects which all other item 'types'
    are based on.
Evan Morgan's avatar
Evan Morgan committed

    You shouldn't use this base class to generate objects unless it is an interactionless
    item, instead use a class that inherits <class GameObject> 
    '''
    id = ""
    name = ""
    can_pickup = False
    can_place = False
    spawnable = {}  # Place Map class as key and probability as value that an item will spawn in a room
    on_interaction = None  # The function in the class that is reponsible for carrying out an interaction
    usable = False
    conflict = False  # This state is changed if the id has been updated due to two items having the same id
Evan Morgan's avatar
Evan Morgan committed

    def __init__(self, id, name, description, pickup=True, place=True):
        '''
        This is the init method, so the method which is called when a new GameObject is generated
        When creating a new GameObject, pass values through GameObject() so that they become properties
        '''
Evan Morgan's avatar
Evan Morgan committed

        self.can_pickup = pickup
        self.can_place = place
        self.description = description
        self.name = name
        self.id = id
Evan Morgan's avatar
Evan Morgan committed

    def inspect(self):
        print(self.name)
        print(self.description)
        print("")

    def _interaction(self):
        '''
        If you want an item with a special purpose (other than a generic purpose, like a weapon)
        you should inherit GameObject and implement this method, also setting on_interaction to this method
        '''

        pass

    def dispose(self, rooms, player):
        # Destroys the item from anywhere where it may exist in the game space
        location = self.get_location(rooms, player)
        if location == "INVENTORY":
            place_in_list = player.inventory.index(self)
            del player.inventory[place_in_list]
        elif location == "EQUIPPED":
            for category in player.equipped:
                item = player.equipped[category]
                if self == item:
                    player.equipped[category] = None

        else:
            place_in_list = rooms[location].items.index(self)
            del rooms[location].items[place_in_list]

    def get_location(self, rooms, player):
        '''
        This function will return the location of a specific (not general) item.
        As it only expects an item to exist in one location at a time, it will not
        return multiple locations. You may also pass just inventory in the case of an interaction
        of an item in the inventory

        If you want multiple versions of one item, consider creating many instances of the class
        '''

        if rooms:
            for room in rooms:
                if self in rooms[room].items:
                    return room

        for category in player.equipped:
            item = player.equipped[category]
            if self == item: return "EQUIPPED"
        
        # If the item can't be found in a room, it must be in someone's inventory
        for item in player.inventory:
            if self == item:
                return "INVENTORY"

        # Else, return nothing as this object does not exist in the game space
        return None

class DurableItem(GameObject):
    '''
    DurableItem is an intermediate class for items (such as defense or attack items) that have durability
    '''
    durability = 0

    def __init__(self, id, name, description, dura, pickup=True, place=True):
        super().__init__(id, name, description, pickup, place)
    def checkout_dura(self, player, rooms, durability_loss):
        For DurableItem, checkout_dura(rooms, player, x: int) will take an integer and remove the amount
        of durability from the item. If the item reaches 0, then the class should be removed from the game space
        '''
        self.durability -= durability_loss

        if self.durability <= 0:
            print(f"{self.name} has broken!")
            self.dispose(rooms, player)
            print(f'{self.name} has lost {durability_loss} durability!')
class Weapon(DurableItem):
    attack = 0
    wobble = 0.1
    def __init__(self, id, name, description, attack, dura, wobble=0, pickup=True, place=True):
        super().__init__(id, name, description, dura, pickup, place)
        self.attack = attack
        self.wobble = wobble

    def inspect(self):
        print(self.name)
        print(self.description)
        print("It is a weapon, and has " + str(self.attack) + " attack power.")
        print("It has a wobble stat of " + str(self.wobble) + ".")
        print("It has " + str(self.durability) + " durability left.\n")
    def get_attack(self):
        '''
        This function is designed to get the attach amount of usage of this item
        Wobble is a percentage of how much the damage will variate around the base value attack
        For example, a wobble of 0.1 (10%) means the value will variate -10% / +10% the attack value
        '''
        
        lower_value = round(self.attack - (self.attack * self.wobble))
        higher_value = round(self.attack + (self.attack * self.wobble))
        return random.randint(lower_value, higher_value)

class Defense(DurableItem):
    defense = 0  # defense is a base value between 0-1 which is a percentage of how much the damage will be reduced
    wobble = 0.0  # wobble is percentage variation (+/-) around the baseline defense percentage
    def __init__(self, id, name, description, defense, dura, wobble=0.0, pickup=True, place=True):
        super().__init__(id, name, description, dura, pickup, place)

        self.defense = defense
        self.wobble = wobble
Evan Morgan's avatar
Evan Morgan committed

    def inspect(self):
        print(self.name)
        print(self.description)
        print("It is a defense item, and has " + str(self.defense) + " defense power.")
        print("It has a wobble stat of " + str(self.wobble) + ".")
        print("It has " + str(self.durability) + " durability left.\n")
    def get_defense(self, base_defense, damage_amount):
        '''
        get_defense(x: int) is designed to see how much of the attack will be defended, aligning to the % defense and
        how much wobble will be applied
        '''

        p_defense_lower = round(self.defense - (self.defense * self.wobble))
        p_defense_higher = round(self.defense + (self.defense * self.wobble))
        percentage_defense = random.randint(p_defense_lower, p_defense_higher)

        final_damage = damage_amount - base_defense - (damage_amount * percentage_defense)
# magic items increase luck
class Magic(GameObject):
    magic = 0
    on_interaction_description = "to use this item"
    equippable = True

    def __init__(self, id, name, description, magic, pickup=True, place=True):
        super().__init__(id, name, description, pickup, place)
    def inspect(self):
        print(self.name)
        print(self.description)
        print("It is a magic item, and has " + str(self.magic) + " magic power.\n")

class HealthItem(GameObject):
    health = 0
    on_interaction_description = "to use this item"
    def __init__(self, id, name, description, health, pickup=True, place=True):
        super().__init__(id, name, description, pickup, place)
    def inspect(self):
        print(self.name)
        print(self.description)
        print("It is a health item, and has " + str(self.health) + " health value.\n")
    def _interaction(self, player):
        # We don't need to heal if the player's health is more than 100
        if player.hp >= player.base_hp:
            print(f"You can't use {self.name} - your health is already at 100 HP!")

            # Don't allow the player's health to get higher than 100
            if player.hp >= player.base_hp:
                player.hp = player.base_hp
            print(f'Success, your health is now {player.hp} HP!')
            self.dispose(None, player)
    on_interaction = _interaction

class Chest(GameObject):
    contains = None   # contains is expected to be a subclass of GameObject, a list or a NoneType
    requires = None   # None means no key or anything is needed, else this should be set to Unlocks class
    opened = False
    on_interaction_description = "to open the chest"

    def __init__(self, id, name, description, contains, requires=None, pickup=False, place=False):
        super().__init__(id, name, description, pickup, place)

        self.contains = contains
        self.requires = requires

    def _interaction(self, player):
        # Upon interacting with a chest item, the player should be awarded the items inside
        # One should pass the inventory list to the function and expect to receive the updated inventory back

        if self.opened:
            print(f'This {self.id.upper()} has already been opened')
        else:
            # Check if the player has the item required to unlock the chest, if needed
            if self.requires:
                if not self.requires in player.inventory:
                    print(f"This {self.id.upper()} is locked. You need something to open it...")
                else:
                    # We need to remove the item if it's an item that destroys itself after use
                    if self.requires.perish_on_open:
                        self.requires.dispose(None, player)

            contains_type = type(self.contains)

            if contains_type == type(None):
                print(f"The {self.id.upper()} was weirdly empty...")
            elif issubclass(contains_type, GameObject): 
                print(f'You found {self.contains.name} in the {self.id.upper()}')

                player.inventory.append(self.contains)
                
            elif contains_type == list:
                # Creates a line that lists out all the items found in the chest. Adds them to inv
                items_string = "You found "
                player.inventory += self.contains

                for item in self.contains:
                    items_string += f'{item.name}, '

                print(items_string[:-2] + f' in the {self.id.upper()}')

            self.opened = True

    on_interaction = _interaction

class Unlocks(GameObject):
    '''
    Unlocks is a special class which is used to unlock locked items, like chests
    '''
    perish_on_open = False   # Should this item get destroyed when it unlocks its target?

    def __init__(self, id, name, description, perish_on_open=False, pickup=True, place=True):
        super().__init__(id, name, description, pickup, place)

        self.perish_on_open = perish_on_open

class GiantSword(Weapon):
    def __init__(self, id):
        super().__init__(
            id,
            description="The swords barely fits in your hand. Surely a relic of a grand warrior",
            attack=40,
            dura=100,
            wobble=0.12,
        )

class WornDagger(Weapon):
    def __init__(self, id):
        super().__init__(
            id,
            description="The blade of this thing has seen better days",
            attack=20,
            dura=25,
            wobble=0.3,
        )

class PlasticSpoon(Weapon):
    def __init__(self, id):
        super().__init__(
            id,
            description="I don't think it'll have much use defending against monsters...",
            attack=2,
            dura=10,
            wobble=0.8,
        )

class GolfClub(Weapon):
    def __init__(self, id):
        super().__init__(
            id,
            description="Turns out the people who lived here were avid golf players.",
            attack=10,
            dura=50,
            wobble=0.2,
        )

class Handgun(Weapon):
    def __init__(self, id):
        super().__init__(
            id,
            name="old handgun",
            description="Does this thing even work?",
            attack=30,
            dura=10,
            wobble=0.9,
        )

class Whip(Weapon):
    def __init__(self, id):
        super().__init__(
            id,
            description="I don't want to know what this was used for... horses?",
            attack=5,
            dura=20,
            wobble=0.1,
        )

class Crocs(Defense):
    def __init__(self, id):
        super().__init__(
            id,
            description="I doubt these will actually soften a blow. Yet their ugly style may distract the monsters",
            defense=0.05,
            dura=20,
            wobble=0.15,
        )

class SturdyShield(Defense):
    def __init__(self, id):
        super().__init__(
            id,
            description="Seems like it can take a blow",
            defense=0.25,
            dura=100,
            wobble=0.05,
        )

class Apron(Defense):
    def __init__(self, id):
        super().__init__(
            id,
            description="Covered in cooking stains. Clearly not cleaned all too well.",
            defense=0.1,
            dura=30,
            wobble=0.2,
        )

class SuitofArmour(Defense):
    def __init__(self, id):
        super().__init__(
            id,
            name="pristine suit of armour",
            description="Whoever owned this before took great care of it. It is still shiny!",
            defense=0.6,
            dura=150,
            wobble=0.05,
        )

class Cookies(HealthItem):
    def __init__(self, id):
        super().__init__(
            id,
            description="Who cares how old these are? All I can think of is the taste of chocolate chip cookies!",
            health=40,
        )

class FirstAidKit(HealthItem):
    def __init__(self, id):
        super().__init__(
            id,
            description="This can heal any ailment at a whim!",
            health=100,
        )

class Apple(HealthItem):
    def __init__(self, id):
        super().__init__(
            id, 
            description="An apple a day keeps the doctors away.",
            health=20,
        )

class StrangePotion(Magic):
    def __init__(self, id):
        super().__init__(
            id,
            description="A vile of unknown contents. Doesn't smell of anything.",
            magic=20,
        )

class Wine(Magic):
    def __init__(self, id):
        super().__init__(
            id,
            description="Bourgogne Pinot Noir 2003. Exquisite.",
            magic=10,
        )

spawnable_items = {
    "weapons": [ GiantSword, WornDagger, PlasticSpoon, GolfClub, Handgun, Whip ],
    "defense": [ Crocs, SturdyShield, Apron, SuitofArmour ],
    "health": [ Cookies, FirstAidKit, Apple ],
    "magic": [ StrangePotion, Wine ]
}