import random class GameObject: ''' This is a base class for game objects which all other item 'types' are based on. 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 = "" description = "" 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 equippable = False usable = False conflict = False # This state is changed if the id has been updated due to two items having the same id 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 ''' self.can_pickup = pickup self.can_place = place self.description = description self.name = name self.id = id def get_name_capitalised(self): ''' Call this function if you want to get the item name starting with a capital Useful for if you're going to put the item at the start of a sentence ''' return self.name[0].upper() + self.name[1:] def inspect(self): print(self.get_name_capitalised()) print(self.description + "\n") 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) self.durability = dura 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) else: print(f'{self.get_name_capitalised()} has lost {durability_loss} durability!') class Weapon(DurableItem): attack = 0 wobble = 0.1 equippable = True 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.get_name_capitalised()) print(self.description) print("It is a weapon, and has " + str(self.attack) + " attack power.") print(f'It has a wobble of +/- {self.wobble * 100}%.') 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 equippable = True 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 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 of +/- " + str(self.wobble * 100) + "%.") 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) return final_damage # 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) self.magic = magic 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" usable = True def __init__(self, id, name, description, health, pickup=True, place=True): super().__init__(id, name, description, pickup, place) self.health = health 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!") else: player.hp += self.health # 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 {0}" 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 self.on_interaction_description = self.on_interaction_description.format(self.id) 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') return 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...") return 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 return 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, name="giant sword", 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, name="worn dagger", 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, name="plastic spoon", 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, name="golf club", 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, name="creaky whip", 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, name="pair of crocs", 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, name="sturdy shield", description="Seems like it can take a blow", defense=0.5, dura=100, wobble=0.05, ) class Apron(Defense): def __init__(self, id): super().__init__( id, name="well used apron", 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, name="pack of cookies", 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, name='first aid kit', description="This can heal any ailment at a whim!", health=100, ) class Apple(HealthItem): def __init__(self, id): super().__init__( id, name="apple", description="An apple a day keeps the doctor away.", health=20, ) class StrangePotion(Magic): def __init__(self, id): super().__init__( id, name="strange potion", description="A vile of unknown contents. Doesn't smell of anything.", magic=20, ) class Wine(Magic): def __init__(self, id): super().__init__( id, name="bottle of wine", description="Bourgogne Pinot Noir 2003. Exquisite.", magic=10, ) class Ambrosia(HealthItem): def __init__(self, id): super().__init__( id, name="plate of ambrosia", description="Dine like a God", health=1000, ) class AppleCrumble(HealthItem): def __init__(self, id): super().__init__( id, name="tray of apple crumble", description="Sounds like a right treat", health=50, ) class SeaFood(HealthItem): def __init__(self, id): super().__init__( id, name="plate of seafood", description="I hope I don\'t have an allergic reaction...", health=25, ) class Chocolate(HealthItem): def __init__(self, id): super().__init__( id, name="bar of chocolate", description="The back reads: \"Expires January 1989\"", health=10, ) class WoodernSword(Weapon): def __init__(self, id): super().__init__( id, name="woodern sword", description="Huh? This is a children's sword...", attack=15, dura=30, wobble=0.1 ) class BattleAxe(Weapon): def __init__(self, id): super().__init__( id, name="battle axe", description="This could be lethal", attack=50, dura=105, wobble=0.05 ) spawnable_items = { "weapons": [ GiantSword, WornDagger, PlasticSpoon, GolfClub, Handgun, Whip, WoodernSword, BattleAxe ], "defense": [ Crocs, SturdyShield, Apron, SuitofArmour ], "health": [ Cookies, FirstAidKit, Apple, Ambrosia, AppleCrumble, SeaFood, Chocolate ], "magic": [ StrangePotion, Wine ] }