# Online Python compiler (interpreter) to run Python online.
# Write Python 3 code in this online editor and run it.
import time
import random as rand
import copy

class Inventory:
    def __init__(self):
        self.itemInventory = ["Health Potion"] * 3
        self.amethysts = 0
        self.emeralds = 0
        self.itemEffects = {}
        
    def defineItemEffects(self):
            self.itemEffects = {
                "Health Potion": "Heal 60",
                "Life Crystal": "MaxHealth 20"
            }

class Player:
    def __init__(self, maxHealth, attackDamage):
        self.maxHealth = maxHealth
        self.health = self.maxHealth
        self.attackDamage = attackDamage
        self.critChance = 0.10
        self.maxEnergy = 3
        self.energy = 0
        self.specialAttacks = {
            "Sweep": ["Attacks all enemies", 2] #attack:[desc, cost]
        }
    
    def __str__(self):
        return self.name
        
class Enemy:
    def __init__(self, maxHealth, attackDamage, name, aDrop, eDrop):
        self.maxHealth = maxHealth
        self.health = self.maxHealth
        self.attackDamage = attackDamage
        self.name = name
        self.eDrop = eDrop
        self.aDrop = aDrop
        self.alive = True
        
    def __str__(self):
        return self.name
        
    #def reset(self):
    #       self.__init__()

def introduction():
    print("Welcome to Text-Based RPG in Python!")
    print("\nIMPORTANT! At the top, on the same row as \"main.py\", there's a fullscreen button. See it? Make sure to click it! If you don't, you won't be able to scroll down to the bottom of the output!\n")
    print("This is a demo. Right now you battle five battles. Try to defeat King Slime!")
    print("The controls are simple. For now, all you need to know is: type in \"a\" for attack, \"i\" for items, and \"o\" for other. \nUsually, just type in the first letter of the option you choose.")
    global name
    name = input("Before we start, what's your name? ")
    if name == "":
        name = "Player"
    if "skibidi" in name.lower():
        name = "Arnav"
    print(f"All right, {name.title()}!")
    p.name = name.title()
    input("Enter key to start!")

#Battle Functions

def playerUI():
    print(f"{p.name}\'s Health: ♥ {p.health}/{p.maxHealth}")
    print(f"You have {inventory.amethysts} Amethysts and {inventory.emeralds} Emeralds")
    
def enemyUI(enemy, number):
    if not best[enemy.name]["Entry"][1]:
        print(f"[{number}] {enemy.name}\'s Health: ♥ {(enemy.health / enemy.maxHealth) * 100}%")
    else:
        print(f"[{number}]{enemy.name}\'s Health: ♥ {enemy.health}/{enemy.maxHealth}")
    
def battleUI():
    print("")
    enemyUI(e1, 1)
    if 'e2' in globals():
        enemyUI(e2, 2)
    if 'e3' in globals():
        enemyUI(e3, 3)
    print(f"{p.name}\'s Health: ♥ {p.health}/{p.maxHealth}")
    print(f"{p.name}\'s Energy: {p.energy}/{p.maxEnergy}")
    
def attack(target, attacker):
    global enemyCount
    if target == p:
        p.health -= attacker.attackDamage
        print(f"\n{attacker.name} attacked you.")
    else:
        if target == 'all':
            if enemyCount == 2:
                e1.health -= p.attackDamage
                e2.health -= p.attackDamage
            elif enemyCount == 3:
                e1.health -= p.attackDamage
                e2.health -= p.attackDamage
                e3.health -= p.attackDamage
            else:
                e1.health -= p.attackDamage
            if rand.random() < p.critChance:
                if enemyCount == 2:
                    e1.health -= p.attackDamage  *.5
                    e2.health -= p.attackDamage *.5
                elif enemyCount == 3:
                    e1.health -= p.attackDamage *.5
                    e2.health -= p.attackDamage *.5
                    e3.health -= p.attackDamage *.5
                else:
                    e1.health -= p.attackDamage *.5
        else:
            target.health -= p.attackDamage
            if rand.random() < p.critChance:
                target.health -= p.attackDamage * .5
                print("\nCritical Hit!")
            else:
                print("\nYou attacked.")
    if target != 'all' and target.health <= 0 :
        target.health = 0
        
def useItem(toUse):
    itemToUse = inventory.itemInventory[toUse]
    effect = inventory.itemEffects[itemToUse]
    effect = effect.split(' ')
    type = effect[0]
    if type == 'Heal':
        p.health += int(effect[1])
        if p.health > p.maxHealth:
            p.health = p.maxHealth
    if type == 'MaxHealth':
        p.maxHealth += int(effect[1])
        p.health += int(effect[1])
    inventory.itemInventory.pop(toUse)

def useInventory():
    print(f"Your Item Inventory: {inventory.itemInventory}")
    item = input("Would you like to use an item? (Type the number of the item in the list, starting at one eg. for first item type 1. Type anything else, or nothing, to exit.) ")
    if item.isdigit() == True: 
        if len(inventory.itemInventory) > int(item) - 1:
            useItem(int(item) - 1)
            return True
        else:
            print("Invalid Number")
            return False
    else:
        return False

def playerTurn():
    playerMove = input("| ️Attack | Item | Other | ").lower()
    time.sleep(0.25)
    if playerMove == "a":
        attackType = ''
        while not (attackType == 'r' or attackType == 's'):
            attackType = input("| ️Regular Attack | Special Attack |  ").lower()
        specialAttackType = ""
        if attackType == 's':
            print("Your special attacks:\n")
            for attacktype, info in p.specialAttacks.items():
                print(f"{attacktype}: {info[0]}, costs {info[1]} energy.")
            specialAttackType = input("\nWhich one would you like to use? (type number, anything else to exit.) ").lower()
            if specialAttackType.isdigit():
                specialAttackType = int(specialAttackType) - 1
            try:
                p.energy -= (p.specialAttacks[list(p.specialAttacks.keys())[specialAttackType]][1])
            except:
                return playerTurn()
            if p.energy < 0:
                p.energy += p.specialAttacks[list(p.specialAttacks.keys())[specialAttackType]][1]
                print("Not enough energy.")
                return playerTurn()
        if specialAttackType == 0:
            attack('all', p)
            return None
        if enemyCount == 1:
            if attackType == "r":
                attack(e1, p)
        else:
            attacking = ""
            while not attacking.isdigit():
                attacking = input("Which enemy would you like to attack? (Type the number): ")
                if attacking.isdigit():
                    if int(attacking) < 1 or int(attacking) > enemyCount:
                        print("Invalid number. Choose again.")
                        attacking = ""
            attacking = int(attacking)
            if attacking == 1:
                attack(e1, p)
            elif attacking == 2:
                attack(e2, p)
            elif attacking == 3:
                attack(e3, p)
    elif playerMove == 'i':
        if not useInventory():
            return playerTurn()
        return playerTurn()
    elif playerMove == 'o':
        otherMove = input("| ️Charge Energy |  ").lower()
        if otherMove == 'c':
            p.energy += 1
            if p.energy > p.maxEnergy:
                p.energy = p.maxEnergy
        else:
            return playerTurn()
    else:
        return playerTurn()

def enemyTurn(enemy):
    if enemy == 1:
        enemy = e1
    elif enemy == 2:
        enemy = e2
    elif enemy == 3:
        enemy = e3
    if enemy.health <= 0:
        enemy.alive = False
        enemy.health = 0
    else:
        time.sleep(rand.randrange(10, 15, 1) / 10)
        attack(p, enemy)
    

def battle():
    global enemyCount
    global e1
    global e2
    global e3
    if 'e2' in globals():
        if 'e3' in globals():
            condition = e1.alive or e2.alive or e3.alive
        else:
            condition = e1.alive or e2.alive
    else: condition = e1.alive
    while (condition == True) and p.health > 0:
        p.energy += 1; 
        if p.energy > p.maxEnergy: 
            p.energy = p.maxEnergy
        battleUI()
        playerTurn()
        time.sleep(0.5)
        battleUI()
        for i in range(1, enemyCount + 1):
            enemyTurn(i)
        if 'e2' in globals():
            if 'e3' in globals():
                condition = e1.alive or e2.alive or e3.alive
            else:
                condition = e1.alive or e2.alive
        else: condition = e1.alive
        time.sleep(0.5)
    
    battleUI()
    
    if p.health <= 0:
        p.health = 0
    if  e1.health <= 0:
        e1.health = 0
    if 'e2' in globals():
        if e2.health <= 0:
            e2.health = 0
    if 'e3' in globals():
        if e3.health <= 0:
            e3.health = 0
    
    if p.health <= 0:
        print('You Lose!')
        quit()
    else:
        print('\nYou Win!')
        if 'e2' in globals():
            if 'e3' in globals():
                inBattle = [e1, e2, e3]
            else:
                inBattle = [e1, e2]
        else: 
            inBattle = [e1]
        for i in inBattle:
            inventory.amethysts += i.aDrop
            print(f'+{i.aDrop} Amethysts')
            if i.eDrop > 0:
                inventory.emeralds += i.eDrop
                print(f'+{i.eDrop} Emeralds')
            if len(i.specialDrops) > 0:
                print(f"Dropped {i.specialDrops}")
                inventory.itemInventory.extend(i.specialDrops)
            if not best[i.name]["Entry"][1]:
                best[i.name]["Entry"][1] = True
                print(f"{i.name} added to bestiary.")

def initiateBattle(wave):
    global e1
    global e2
    global e3
    global enemyCount
    e1 = copy.copy(waves[wave][1]["enemy"])
    e1.specialDrops = waves[wave][1]["specialDrops"]
    enemyCount = 1
    if len(waves[wave]) > 1:
        if waves[wave][2]["enemy"] == waves[wave][1]["enemy"]:
            e2 = copy.copy(e1)
        else:
            e2 = waves[wave][2]["enemy"]
        e2.specialDrops = waves[wave][2]["specialDrops"]
        enemyCount += 1
        if len(waves[wave]) == 3:
            if waves[wave][3]["enemy"] == waves[wave][1]["enemy"]:
                e3 = copy.copy(e1)
            if waves[wave][3]["enemy"] == waves[wave][2]["enemy"]:
                e3 = copy.copy(e2)
            e3 = waves[wave][3]["enemy"]
            e3.specialDrops = waves[wave][3]["specialDrops"]
            enemyCount += 1
        else:
            try:
                del e3
            except:
                pass
    else:
        try:
            del e2
            del e3
        except:
            pass
    
    
    if e1.name == "King Slime":
        print("\nBOSS BATTLE!")
    if enemyCount == 2:
        print(f"\n{p.name} vs. {e1.name}, {e2.name}")
    elif enemyCount == 3:
        print(f"\n{p.name} vs. {e1.name}, {e2.name}, {e3.name}")
    else:
        print(f"\n{p.name} vs. {e1.name}")

#End of batte functions 

def viewBestiary():
    print("\nEnemies in your bestiary:")
    global best
    viewable = []
    for enemy in best:
        if best[enemy]["Entry"][1] == False:
                continue
        print(enemy)
        viewable.append(enemy)
    view = input("\nWhich entry would you like to view? (Type position of enemy in the list of enemies, eg. first type \"1\". Type anything else, or nothing, to exit.) ")
    if view.isdigit() == True:
        if int(view) - 1 < len(viewable):
            view = int(view) - 1
            print()
            print(viewable[view])
            print(best[viewable[view]]["Entry"][0])
        else:
            print("Invalid Number")

def rest():
    playerUI()
    exit = False
    while exit == False:
        print("\nWhat would you like to do?")
        choice = input("| Item | View Bestiary | (i for item, b for bestiary, anything else to exit) ").lower()
        if choice == "i":
            while useInventory():
                print()
                playerUI()
        elif choice == 'b':
            viewBestiary()
        else:
            exit = True;


print("Text-Based RPG in Python, by Maxwell")
p = Player(100, 20)
inventory = Inventory()
inventory.defineItemEffects()
global playerMove


best = { 
    "Orange Slime": {
        "Enemy": Enemy(40, 10, "Orange Slime", 2, 0),
        "Entry": ["""Health: 40, Attack Damage: 10
        Just your regular orange slime boi.""", False]
    },
    "Blue Slime": {
        "Enemy": Enemy(60, 10, "Blue Slime", 3, 0),
        "Entry": ["""Health: 60, Attack Damage: 10
        Slightly more durable than the orange slime. Also, comes in a cool shade of blue! Although you can't really see that...""", False]
    },
    "King Slime": {
        "Enemy": Enemy(120, 20, "King Slime", 5, 1),
        "Entry": ["""Health: 120, Attack Damage: 20
        [BOSS] A king among slimes. Very strong, very large, and rocks a cool crown.""", False]
    }
} #Short for "bestiary", basically a monster encyclopedia

waves = {
    1 : {
        1 : {
            "enemy" : best["Orange Slime"]["Enemy"],
            "specialDrops" : []
        }
    },
    2 : {
        1 : {
            "enemy" : best["Blue Slime"]["Enemy"],
            "specialDrops" : []
        }
    },
    3 : {
        1 : {
            "enemy" : best["Orange Slime"]["Enemy"],
            "specialDrops" : []
        },
        2 : {
            "enemy" : best["Orange Slime"]["Enemy"],
            "specialDrops" : []
        }
    },
    4 : {
        1 : {
            "enemy" : best["Blue Slime"]["Enemy"],
            "specialDrops" : []
        },
        2 : {
            "enemy" : best["Blue Slime"]["Enemy"],
            "specialDrops" : ["Health Potion"]
        }
    },
    5 : {
        1 : {
            "enemy" : best["King Slime"]["Enemy"],
            "specialDrops" : ["Life Crystal"]
        }
    }
}

introduction()

for wave in range(1, len(waves) + 1):
    initiateBattle(wave)
    battle()
    rest()
print(f"\nCongrats, {p.name}! You've beaten the demo.")



# To Do: 








#████████╗███████╗██╗  ██╗████████╗                                   
#╚══██╔══╝██╔════╝╚██╗██╔╝╚══██╔══╝                                   
#   ██║   █████╗   ╚███╔╝    ██║       █████╗                         
#   ██║   ██╔══╝   ██╔██╗    ██║       ╚════╝                         
#   ██║   ███████╗██╔╝ ██╗   ██║                                      
#   ╚═╝   ╚══════╝╚═╝  ╚═╝   ╚═╝                                      
#                                                                     
#██████╗  █████╗ ███████╗███████╗██████╗     ██████╗ ██████╗  ██████╗ 
#██╔══██╗██╔══██╗██╔════╝██╔════╝██╔══██╗    ██╔══██╗██╔══██╗██╔════╝ 
#██████╔╝███████║███████╗█████╗  ██║  ██║    ██████╔╝██████╔╝██║  ███╗
#██╔══██╗██╔══██║╚════██║██╔══╝  ██║  ██║    ██╔══██╗██╔═══╝ ██║   ██║
#██████╔╝██║  ██║███████║███████╗██████╔╝    ██║  ██║██║     ╚██████╔╝
#╚═════╝ ╚═╝  ╚═╝╚══════╝╚══════╝╚═════╝     ╚═╝  ╚═╝╚═╝      ╚═════╝ 