Skip to content
Snippets Groups Projects
game.py 19.2 KiB
Newer Older
Evan Morgan's avatar
Evan Morgan committed
#!/usr/bin/python3

from map import rooms
Evan Morgan's avatar
Evan Morgan committed
from player import *
Evan Morgan's avatar
Evan Morgan committed
from items import *
from gameparser import *
Evan Morgan's avatar
Evan Morgan committed
from enemies import *
from item_dist import *
from enemies_dist import populate_enemies
Evan Morgan's avatar
Evan Morgan committed
import random

def display_inventory():
Evan Morgan's avatar
Evan Morgan committed
    while True:
        print("Weapon equipped:", player.get_equipped_name("weapon"))
        print("Defense equipped:", player.get_equipped_name("defense"))
        print("Magic item equipped:", player.get_equipped_name("magic"))
Evan Morgan's avatar
Evan Morgan committed
        print("")
Evan Morgan's avatar
Evan Morgan committed
            print("You have ", end = "")
            for x in range(len(player.inventory)-1):
                print(player.inventory[x].name, end = ", ")
            print(player.inventory[len(player.inventory)-1].name, end = ".")
            print("\n")
        print("The commands you can use are:")

        for item in player.inventory:
            print("INSPECT", item.id.upper(), "to inspect", item.name)

        for category in player.equipped:
            item = player.equipped[category]
            if item == None: continue
            print("INSPECT", item.id.upper(), "to inspect", item.name)

        for item in player.inventory:
            if item.usable == True:
                print("USE", item.id.upper(), "to use", item.name)

        for item in player.inventory:
            if item.equippable == True:
                print("EQUIP", item.id.upper(), "to equip", item.name)
        
        for category in player.equipped:
            item = player.equipped[category]
            if item == None: continue
            print("UNEQUIP", item.id.upper(), "to unequip", item.name)

        print("EXIT to exit the inventory sub-menu.")

        user_input = input(">")
Evan Morgan's avatar
Evan Morgan committed
        user_input = normalise_input(user_input)
Evan Morgan's avatar
Evan Morgan committed
        if user_input[0] == "inspect":
            if len(user_input) == 1:
                print("You must specify something to inspect.")
            else:
                player.inspect_item(user_input[1])
        if user_input[0] == "use":
            if len(user_input) == 1:
                print("You must specify something to use.")
            else:
                # find item
                item_to_use = None
                for item in player.inventory:
                    if item.id == user_input[1]:
                        item_to_use = item
                        break

                if item_to_use == None:
                    print("Could not find that item to use.")
                
                elif item_to_use.usable == True:
                    item.on_interaction(player)

                else:
                    print("Could not use that item.")

Evan Morgan's avatar
Evan Morgan committed
        if user_input[0] == "equip":
            if len(user_input) == 1:
                print("You must specify something to equip.")
            else:
                player.equip(user_input[1])

        if user_input[0] == "unequip":
            if len(user_input) == 1:
                print("You must specify something to unequip.")
            else:
                player.unequip(user_input[1])
Evan Morgan's avatar
Evan Morgan committed
        if user_input[0] == "exit":
            break
    
Evan Morgan's avatar
Evan Morgan committed
def display_map():
Thomas Glasper's avatar
Thomas Glasper committed
    # display mini-map - uncleared rooms do not display names, just question marks
    print("You inspect your map, here is what the relevant part says.")
    print("Question marks represent rooms you haven't cleared yet.\n\n")
Thomas Glasper's avatar
Thomas Glasper committed

    start_point = player.current_room
    information = {
        "current": {
            "exists": True,
            "name": start_point.name,
        }
    }

    # gather information about rooms we need to print
    for direction in ["north", "east", "south", "west"]:
        if direction in start_point.exits:
            exists = True
        else:
            exists = False

        information[direction] = {
            "exists": exists,
        }

        if exists == True:
            room_directed_to_id = start_point.exits[direction]
            room = rooms[room_directed_to_id]

            if room.cleared == True:
                information[direction]["name"] = room.name
            else:
                information[direction]["name"] = "?????"

    # print the information
    north_name_offset = 0
    north_arrow_offset = len(information["current"]["name"]) // 2
    if information["west"]["exists"] == True and information["north"]["exists"] == True:
        north_name_offset = 8 + len(information["west"]["name"]) + len(information["current"]["name"]) // 2 - len(information["north"]["name"]) // 2
        north_arrow_offset = 8 + len(information["west"]["name"]) + len(information["current"]["name"]) // 2

    if information["north"]["exists"] == True:
        print(" " * north_name_offset + information["north"]["name"])

        print(" " * north_arrow_offset + "^")
        for i in range(2):
            print(" " * north_arrow_offset + "|")

    middle_print_line = ""
    if information["west"]["exists"] == True: 
        middle_print_line = middle_print_line + information["west"]["name"] + " <----- " 

    middle_print_line += information["current"]["name"]

    if information["east"]["exists"] == True:
        middle_print_line = middle_print_line + " -----> " + information["east"]["name"] 

    print(middle_print_line)

    south_name_offset = 0
    south_arrow_offset = len(information["current"]["name"]) // 2
    if information["west"]["exists"] == True and information["south"]["exists"] == True:
        south_name_offset = 8 + len(information["west"]["name"]) + len(information["current"]["name"]) // 2 - len(information["south"]["name"]) // 2
        south_arrow_offset = 8 + len(information["west"]["name"]) + len(information["current"]["name"]) // 2

    if information["south"]["exists"] == True:
        for i in range(2):
            print(" " * south_arrow_offset + "|")
        print(" " * south_arrow_offset + "v")
        
        print(" " * south_name_offset + information["south"]["name"])

    input("\n\n> Press any key to continue.")

def display_help():
    menus = {
        "items": {
            "label": "items",
            "lines": [
                "While travelling around the world you will encounter items. Items can be picked up and stored in your inventory.",
                "To see the items you have picked up, you can open the inventory sub-menu. You can also drop items you don't want...",
                "anymore. In the inventory menu you will also see your equipped items - some items can be equipped to certain slots...",
                "to help you during battle. Some items can also be used in the inventory sub-menu."
            ]
        },
        "battle": {
            "label": "battle information",
            "lines": [
                "When you encounter an enemy, you will begin a battle with them. Battle is based off a turn-based system. You will get...",
                "a chance to attack, then the enemy will attack. The damage your attack does is based off your weapon and attack base stats and...",
                "the enemy's defensive stats. The enemy's damage is based off it's base attack stats and your defensive base stat and...",
                "defensive items. You can choose from a number of moves, each with different benefits and drawbacks. Based off your base luck...",
                "and magic item you have a chance of getting a critical hit which increases damage or missing entirely and doing no damage. The...",
                "enemy has the same mechanics, but based just off their base luck stat. Each attack costs your equipment durability - when durability...",
                "of an item reaches zero, it will break."
            ],
        },
    }

    while True:
        print("\nWhich help sub-menu do you wish to display?")
        print("You can use the following commands:")
        for menu in menus:
            menu_data = menus[menu]
            print(menu.upper() + " to display " + menu_data["label"] + " menu.")
        print("EXIT to exit the help sub-menu.")

        user_input = input(">")
        user_input = normalise_input(user_input)

        if user_input[0] == "exit":
            break

        for menu in menus:
            if user_input[0] == menu:
                to_display = menus[menu]

                for line in to_display["lines"]:
                    print(line)

Evan Morgan's avatar
Evan Morgan committed

def combat(enemy):
    print("You have encountered a level " + str(enemy.level), str(enemy.name) + "!")

Evan Morgan's avatar
Evan Morgan committed
    while True:
        print(enemy.name + " has " + str(enemy.hp) + " hp remaining.")
Evan Morgan's avatar
Evan Morgan committed
        print("")
        print("You have " + str(player.hp) + " hp remaining.\n")
        
        print("What would you like to do?")
        print("The commands you can use are:")
        print("ATTACK to perform a normal attack.")
        print("ATTACK STRONG to perform a strong attack with a chance to miss.")
        print("ATTACK QUICK to perform a quick attack.")
        print("INVENTORY to view your inventory, to switch weapons, inspect weapons, use items etc.")
        
Evan Morgan's avatar
Evan Morgan committed
        user_input = input(">")
        user_input = normalise_input(user_input)

        if user_input[0] == "inventory" or user_input[0] == "inv":
Evan Morgan's avatar
Evan Morgan committed

        elif user_input[0] == "attack":
            # normal attack
            if len(user_input) == 1:
                print("You attacked using a normal attack!")

                missed = player.check_attack_missed()
                if missed == True:
                    print("Your attack missed. You did 0 damage!")

                else:
                    attack_damage, critical = player.get_damage()
                    damage_to_do = int(max(enemy.apply_defense(attack_damage), 0))

                    if critical == True:
                        print("Critical hit! Your attack did " + str(damage_to_do) + " damage!")
                    else:
                        print("Your attack did " + str(damage_to_do) + " damage!")
                    
                    enemy.take_damage(damage_to_do)
                    took_dura = player.take_attack_wear(2)
                    if took_dura: print("Your attack spent 2 durability!")

            # strong attack does more damage, but costs more durability, and has higher chance of missing
            elif len(user_input) > 1 and user_input[1] == "strong":
                print("You attacked using a strong attack!")
Evan Morgan's avatar
Evan Morgan committed
                
                missed = player.check_attack_missed(1.25)
                if missed == True:
                    print("Your attack missed. You did 0 damage!")

                else:
                    attack_damage, critical = player.get_damage(1.25)
                    damage_to_do = int(max(enemy.apply_defense(attack_damage), 0))

                    if critical == True:
                        print("Critical hit! Your attack did " + str(damage_to_do) + " damage!")
                    else:
                        print("Your attack did " + str(damage_to_do) + " damage!")
                    
                    enemy.take_damage(damage_to_do)
                    took_dura = player.take_attack_wear(3)
                    if took_dura: print("Your attack spent 3 durability!")

            # quick attack does a series of smaller attacks
            elif len(user_input) > 1 and user_input[1] == "quick":
                print("You attacked using a series of quick attacks!")
                for i in range(3):
                    missed = player.check_attack_missed(0.3)
                    if missed == True:
                        print("Your attack missed. You did 0 damage!")

                    else:
                        attack_damage, critical = player.get_damage(0.3)
                        damage_to_do = int(max(enemy.apply_defense(attack_damage), 0))

                        if critical == True:
                            print("Critical hit! Your attack did " + str(damage_to_do) + " damage!")
                        else:
                            print("Your attack did " + str(damage_to_do) + " damage!")
                        
                        enemy.take_damage(damage_to_do)
                        took_dura = player.take_attack_wear(1)
                        if took_dura: print("Your attack spent 1 durability!")
Evan Morgan's avatar
Evan Morgan committed

            else:
                print("Could not perform that type of attack.")
Evan Morgan's avatar
Evan Morgan committed

            # check if you killed enemy
            if enemy.alive == False:
                print("You defeated the " + enemy.name + "!\n")
                player.gain_xp(enemy.level * 5)
Evan Morgan's avatar
Evan Morgan committed

            # now calculate the enemies attack
            print("")
            print("The " + enemy.name + " will now attack.")

            enemy_attack_damage, critical = enemy.get_attack()
            damage_to_do = int(max(player.apply_defense(enemy_attack_damage), 0))

            if critical == True:
                print("Critical hit! The enemy did " + str(damage_to_do) + " damage!")
            else:
                print("The enemy did " + str(damage_to_do) + " damage!")

            player.take_defense_wear(damage_to_do // 10)
            player.take_damage(damage_to_do)

            # check if you died
            if player.alive == False:
                print("You died!\n")
                break
        
        else:
            print("Could not recognise that command.")
Evan Morgan's avatar
Evan Morgan committed

def puzzle():
Evan Morgan's avatar
Evan Morgan committed

def chest():
Evan Morgan's avatar
Evan Morgan committed

def boss_battle():
Evan Morgan's avatar
Evan Morgan committed
    
def list_of_items(items):
    if len(items) > 0:
        print("'", end = "")
        for x in range(len(items)-1):
            print(items[x]["name"], end = ", ")
        print(items[len(items)-1]["name"], end = "'")
    else:
        return ''
    
def print_room_items(room):
Evan Morgan's avatar
Evan Morgan committed
        print("There is ", end = "")
        for x in range(len(room.items)-1):
            print(room.items[x].name, end = ", ")
        print(room.items[len(room.items)-1].name, end = " ")
Evan Morgan's avatar
Evan Morgan committed
        print("here.")
        print("")

def print_inventory_items(items):
    if len(items) > 0:
        print("You have ", end = "")
        for x in range(len(items)-1):
            print(items[x].name, end = ", ")
        print(items[len(items)-1].name, end = ".")
Evan Morgan's avatar
Evan Morgan committed
        print("\n")

def print_room(room):
    print("")
    print(room.name)
    print(room.description + "\n")
Evan Morgan's avatar
Evan Morgan committed
    print_room_items(room)

def exit_leads_to(exits, direction):
    return rooms[exits[direction]].name
Evan Morgan's avatar
Evan Morgan committed

def print_exit(direction, leads_to):
    print("GO " + direction.upper() + " to " + leads_to + ".")

def print_menu(exits, room_items, inv_items):
Thomas Glasper's avatar
Thomas Glasper committed
    print("The commands you can use are:")
    
Evan Morgan's avatar
Evan Morgan committed
    # Iterate over available exits
    for direction in exits:
        # Print the exit name and where it leads to
        print_exit(direction, exit_leads_to(exits, direction))
Thomas Glasper's avatar
Thomas Glasper committed

    for item in room_items:
        if item.can_pickup is True:
            print("TAKE", item.id.upper(), "to take a", item.name + ".")
        elif item.on_interaction is not None:
            # In this case, it's an item we must interact with in the game space as it cannot be picked up
            print(f"INTERACT {item.id.upper()} {item.on_interaction_description}.")
Thomas Glasper's avatar
Thomas Glasper committed

    for item in inv_items:
        if item.can_place is True:
            print("DROP", item.id.upper(), "to drop your", item.name + ".")
Thomas Glasper's avatar
Thomas Glasper committed
    
    print("INVENTORY to view your inventory")
    print("MAP to view the map")
    print("HELP for help information")
Evan Morgan's avatar
Evan Morgan committed

def execute_go(direction):
    possible_exits = player.current_room.exits
    if direction in possible_exits:
        new_room_name = possible_exits[direction]
        new_room = rooms[new_room_name]
        print("You are moving to", new_room.name)
        player.current_room = new_room
    else:
        print("You cannot go there.")
Evan Morgan's avatar
Evan Morgan committed

def execute_take(item_id):
    for item in player.current_room.items:
        if item.id == item_id and item.can_pickup is True:
            player.inventory.append(item)
            player.current_room.items.remove(item)
Evan Morgan's avatar
Evan Morgan committed
            return
Evan Morgan's avatar
Evan Morgan committed
    print("You cannot take that")

def execute_drop(item_id):
Thomas Glasper's avatar
Thomas Glasper committed
    for item in player.inventory:
        if item.id == item_id and item.can_place is True:
            player.inventory.remove(item)
            player.current_room.items.append(item)
Evan Morgan's avatar
Evan Morgan committed
            return
Evan Morgan's avatar
Evan Morgan committed
    print("You cannot drop that")

def execute_interact(item_id):
    for item in player.current_room.items:
        if item.id == item_id and item.on_interaction is not None and not item.can_pickup:
            # We need to check the item can't be picked up as then the on_interation is dedicated to use inside the inv
            item.on_interaction(player)
            return

    print("You cannot interact with that")

Evan Morgan's avatar
Evan Morgan committed
def execute_command(command):
    """This function takes a command (a list of words as returned by
    normalise_input) and, depending on the type of action (the first word of
    the command: "go", "take", or "drop"), executes either execute_go,
    execute_take, or execute_drop, supplying the second word as the argument.

    """

Evan Morgan's avatar
Evan Morgan committed
        return

    if command[0] == "go":
        if len(command) > 1:
            execute_go(command[1])
        else:
            print("Go where?")

    elif command[0] == "take":
        if len(command) > 1:
            execute_take(command[1])
        else:
            print("Take what?")

    elif command[0] == "drop":
        if len(command) > 1:
            execute_drop(command[1])
        else:
            print("Drop what?")

    elif command[0] == "inventory" or command[0] == "inv":
Evan Morgan's avatar
Evan Morgan committed
        display_inventory()
Evan Morgan's avatar
Evan Morgan committed

    elif command[0] == "map":
        display_map()

    elif command[0] == "help":
        display_help()

    elif command[0] == "interact":
        execute_interact(command[1])

Evan Morgan's avatar
Evan Morgan committed
    else:
        print("This makes no sense.")

def menu(exits, room_items, inv_items):
    # Display menu
    print_menu(exits, room_items, inv_items)

    # Read player's input
    user_input = input("> ")

    # Normalise the input
    normalised_user_input = normalise_input(user_input)

    return normalised_user_input

def move(exits, direction):
    # Next room to go to
    return rooms[exits[direction]]

# This is the entry point of our program
def main():
    # Generate random items in each room before we start the game
    populate_rooms()
    populate_enemies()
    print("You suddenly wake up in an ancient abandoned manner house. Every window boarded, every door to the real world locked...")
    print("The old house finds itself inhabited by a plethora of strange monsters, of which many seem violent. It is up to you...")
    print("to trek forward and find a way out with what little you have - avoiding death as if it were around every corner.")
    input("> Press enter to begin the game.")
Evan Morgan's avatar
Evan Morgan committed

    # Main game loop
    while True:
        # Display game status (room description, inventory etc.)
        print_room(player.current_room)
        if player.current_room.cleared == False:
            for enemy in player.current_room.enemies:
                combat(enemy)

                if player.alive == False:
                    break
Evan Morgan's avatar
Evan Morgan committed

            if player.alive == False:
                break
            else:
                player.current_room.cleared = True

Evan Morgan's avatar
Evan Morgan committed
        # Show the menu with possible actions and ask the player
        command = menu(player.current_room.exits, player.current_room.items, player.inventory)
Evan Morgan's avatar
Evan Morgan committed

        # Execute the player's command
        execute_command(command)
Evan Morgan's avatar
Evan Morgan committed
        
    # player death message
    if player.alive == False:
        print("Game over. You did not manage to beat the game. Try again.")
    
Evan Morgan's avatar
Evan Morgan committed
if __name__ == "__main__":
Evan Morgan's avatar
Evan Morgan committed
    main()