using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;

public enum BattleState { START, PLAYERTURN, ENEMYTURN, WON, LOST, FLEE }

public class ConfrontSystem : MonoBehaviour
{
    public GameObject player;
    public GameObject enemy;
    public GameObject enemyDeath;

    Unit playerUnit;
    Unit enemyUnit;

    public BattleState state;
    public GameObject confrontScreen;
    public GameObject confrontInstructions;

    public AudioSource battleSFX;
    public AudioClip confront;
    public AudioClip stab;
    public AudioClip isHurt;
    public AudioClip hover;
    public AudioClip select;

    private Text dialogueTxt;
    private bool playerIsReady = false;
    private ProgressManager progress;
    private Animator effects;
    private Animator bulwarkFX;
    private Animator rivetFX;
    private int guardCount = 0;
    private int atkUpCount = 0;
    private bool parryAttempt = false;
    private SceneLoader sl;
    private Jukebox jukebox;




    private string[] allPlayerChoices = { "A B S C O N D", "C O N F R O N T", "B U L W A R K", "R I V E T" }; // DeParry
    private int playerChoiceIdx = 0;

    void Start()
    {
        state = BattleState.START;
         playerUnit = GameObject.Find("Player").GetComponent<Unit>();
        progress = GameObject.Find("ProgressManager").GetComponent<ProgressManager>();
        effects = GameObject.Find("HUD").transform.Find("HUDEffects").GetComponent<Animator>();
        bulwarkFX = effects.gameObject.transform.Find("bulwark").GetComponent<Animator>();
        rivetFX = effects.gameObject.transform.Find("rivet").GetComponent<Animator>();
        sl = GameObject.Find("SceneManager").GetComponent<SceneLoader>();
        jukebox = GameObject.Find("Jukebox").GetComponent<Jukebox>();
    }

    void Update()
    {
        //Debug.Log(playerChoice);

        if (state == BattleState.PLAYERTURN)
        {
            if (Input.GetKeyDown(KeyCode.Space) && playerIsReady) // Select the current option
            {
                // Make text white again
                dialogueTxt.color = new Color(255, 255, 255);
                confrontInstructions.SetActive(false);

                // Play SFX
                battleSFX.PlayOneShot(select, 2);

                if (playerChoiceIdx == 0) // Abscond (flee)
                {
                    Debug.Log("Absconding");
                    state = BattleState.FLEE;
                    AttemptFlee();
                    
                }
                else if (playerChoiceIdx == 1) // Confront (attack)
                {
                    Debug.Log("Confronting");
                    onPlayerAtk();
                }
                else if (playerChoiceIdx == 2) // Bulwark (guard)
                {
                    guardCount += 3;
                    Debug.Log("Bulwarking. " + guardCount + " turns left bulwarking");

                    // Show bulwark UI
                    bulwarkFX.SetBool("isBulwarking", true);

                    // Go to enemy's turn
                    state = BattleState.ENEMYTURN;
                    StartCoroutine(EnemyTurn());
                }
                else if (playerChoiceIdx == 3) // Rivet (attack up)
                {
                    Debug.Log("Riveting");
                    atkUpCount += 3;

                    // Show rivet UI
                    rivetFX.SetBool("isRiveting", true);

                    // Go to enemy's turn
                    state = BattleState.ENEMYTURN;
                    StartCoroutine(EnemyTurn());
                }
                /*else if (playerChoiceIdx == 4) // Parry
                {
                    Debug.Log("Parrying");
                    parryAttempt = true;

                    // Go to enemy's turn
                    state = BattleState.ENEMYTURN;
                    StartCoroutine(EnemyTurn());
                }*/

                playerIsReady = false;

                // Revert player's choice back to default of abscond
                playerChoiceIdx = 0;
            }

            if (Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.D))
            {

                battleSFX.PlayOneShot(hover, 2);

                if (playerChoiceIdx + 1 > allPlayerChoices.Length - 1) 
                {
                    playerChoiceIdx = 0;
                }
                else
                {
                    playerChoiceIdx += 1;
                }

                // Display choices on UI
                showDialogue(allPlayerChoices[playerChoiceIdx]);

                //Debug.Log(playerChoiceIdx + " " + allPlayerChoices[playerChoiceIdx]);
            }

        }
    }

    public void startConfrontation(GameObject enemy)
    {
        StartCoroutine(SetupBattle(enemy));
    }

    IEnumerator SetupBattle(GameObject enemy)
    {

        // Enable battle music & SFX
        battleSFX.PlayOneShot(confront);
        jukebox.playBattle();

        // Disable player and enemy movement 
        player.GetComponent<FirstPersonAIO>().playerCanMove = false;
        enemy.GetComponent<NavMeshAgent>().isStopped = true;

        // Make the enemy chatter
        string intro1 = enemy.GetComponent<Monster>().intro1;
        string intro2 = enemy.GetComponent<Monster>().intro2;
        confrontScreen.GetComponent<MakeChatter>().ShowChatter(intro1, intro2);
        

        // Enable HUD
        confrontScreen.SetActive(true);

        enemyUnit = enemy.GetComponent<Unit>();
        player.tag = "Hiding"; //Other enemies won't detect the player while they're in battle
        dialogueTxt = confrontScreen.transform.Find("DialogueTxt").GetComponent<Text>();

        showDialogue(enemyUnit.name + " emerges from the darkness . . .");

        // Wait until dialogue finishes animating
        yield return new WaitForSeconds((0.05f * (enemyUnit.name + " emerges from the darkness . . .").Length) + 1f);

        // The enemy goes first
        state = BattleState.ENEMYTURN;
        StartCoroutine(EnemyTurn());
    }

    void PlayerTurn()
    {
        //showDialogue("Fate is in your hands. (WASD + SPACE)");
        confrontInstructions.SetActive(true);

        // Display choices on UI
        dialogueTxt.color = new Color(255, 0, 0);

        // Count down on rivet
        if (atkUpCount > 0)
        {
            atkUpCount -= 1;
            Debug.Log(atkUpCount + " turns left for riveting");

            // Remove rivet UI if out of rivets
            if (atkUpCount <= 0)
            {
                rivetFX.SetBool("isRiveting", false);
            }
        }

        if (!playerIsReady) // Ensuers player cannot select choice before seeing it
        {
            playerIsReady = true;
        }

        showDialogue(allPlayerChoices[playerChoiceIdx]);
    }

    IEnumerator EnemyTurn()
    {
        dialogueTxt.color = new Color(255, 255, 255);

        if (guardCount > 0)
        {
            showDialogue("Your hold your arms up to dampen the blow.");
            yield return new WaitForSeconds(0.05f * "Your hold your arms up to dampen the blow.".Length);
        }

        
        yield return new WaitForSeconds(1f);
        enemy.GetComponent<Monster>().facing = false;

        // All available actions any enemy can take. Name of action => {damage dealt, "description"}
        var allEnemyActions = new Dictionary<string, object[]>()
        {
            // Frimphew
            {"nothing1", new object[]{ 0, " waits for you in pity.", "I WILL WAIT." } },
            {"shroud", new object[] {10, " surroundeds your entire body. Oh god, it's so cold.", "IT IS HARD TO THINK IN THESE CONDITIONS." } },
            {"stare1", new object[] {4, " stares at you unwavering. Chills run down your spine.", "IT’S EASY TO BE EMPTY." } },
            {"mutate", new object[] {6, " takes the form of someone familiar, and stabs you deep in the gut.", "YOU’D NEVER HURT ME, WOULD YOU?" } },

            // Siite
            {"nothing2", new object[]{ 0, " waits for you in pity.", "I'm getting sleepy, I think." } },
            {"blink1", new object[] {5, " sets its horrible gaze upon you. You feel its eyes burn your flesh.", "It’s a bit of dust, it irritates me."} },
            {"cry1", new object[]{ 2, " weeps hot tears. It gets on your body.", "Many tears, many fears." } },


            // Kenthu
            {"laugh1", new object[] {3, " lets out a bellowing laugh. Your ears feel hot.", "~I’m not laughing with you~" } },
            {"shake", new object[] {0, " shakes its head disapprovingly.", "~You’ll never make friends looking like that. But then again, what do I know~" } },
            {"turn", new object[] {0, " turns its back from you.", "~You don’t make any sense~" } },
            {"hit", new object[] {5, " hits you hard somewhere on your body. You don't remember where.", "~It wasn’t that bad~" } },

            // Treachoy
            {"blink2", new object[] {5, " sets its horrible gaze upon you. You feel its eyes burn your flesh.", "I    d o n ‘ t    w a n t    a    b o d y . . ." } },
            {"cry2", new object[]{ 2, " weeps hot tears. It gets on your body.", " I   c a n ‘ t    s t o p    i t . . ." } },
            {"smile", new object[] {0, " gives you a little, teeny, tiny grin.", "M y   g u m s   b l e e d    w h e n   I   b r u s h   m y   t e e t h . . ." } },
            {"bite", new object[] {5, " bites you fiercely. Your bones crack under the pressure.", " D e l i c i o u s   f l e s h . . . N e e d s   s a l t . . ." } },
            {"sniff", new object[] { 0, " cautiously sniffs you. It thinks you smell a little bad.", "Sniff: R e m i n d s    m e    o f   m y    g r a n d m o t h e r . . ." } },

            // Minkee
            {"laugh2", new object[] {3, " lets out a piercing laugh. Your ears feel hot.", "So funny!!!!! So very funny!!!!! Do it again!!!!!!" } },
            {"stare2", new object[] {4, " stares at you unwavering. Chills run down your spine.", "I can’t help it!!!!!" } },
            {"cry3", new object[]{ 2, " weeps hot tears. It gets on your body.", "Tell me you love me!!!!!! Don’t leave me!!!!!!!" } },
            {"dance", new object[]{ 0, " dances around in a ridiculous manner.", "Yay!!!!! Yay yay yay yay!!!!!!" } },

            // Crawler
            {"meat", new object[] {10, " tears into your body, my the organ and sinew and bones that crunch.", "words" } },
            {"skin", new object[] {10, " skins you, skins you, skins you, and skins you.", "words" } },
            {"slam", new object[] {8, " pounds its fists onto the floor in a tantrum.", "words" } },
            {"marinate", new object[] {0, " marinates its meat in red juice.", "words" } },
            {"moisturize", new object[] {9, " wets its fingers with your stomach.", "words" } },
            {"pop", new object[] {5, " pops your fingers one by one. Pop! Pop! Pop! Pop! Pop!", "words" } },
            {"beg", new object[] {2, " wriggles pathetically on the floor.", "words" } },
            {"hug", new object[] {7, " wraps its arms around you in a tight hug. Your ribs crack.", "words" } },
            {"speak", new object[] {5, " tells you things you don't want to hear.", "words" } },
            {"bleed", new object[] {2, " sits and bleeds and sits and bleeds.", "words" } },
        };

        // Get all available actions THIS SPECIFIC enemy can take
        var actions = enemyUnit.actions;

        // Choose a random action to take
        string action = actions[UnityEngine.Random.Range(0, actions.Count)];

        // Monster cry
        enemy.GetComponent<AudioSource>().Play();

        // Attack chatter
        confrontScreen.GetComponent<MakeChatter>().currentChatters = 0;
        confrontScreen.GetComponent<MakeChatter>().ShowChatter(allEnemyActions[action][2].ToString(), "");

        // Show attack animation
        ShowAttack(action);

        // Show attack dialogue
        showDialogue(enemyUnit.name + allEnemyActions[action][1]);
        yield return new WaitForSeconds((0.05f * (enemyUnit.name + allEnemyActions[action][1]).Length) + 1f);

        float enemyDmg = Convert.ToSingle((int)allEnemyActions[action][0]);

        // Account for parrying
        /*if (parryAttempt)
        {
            if (enemyDmg > 0)
            {
                Debug.Log("parry succeeds!");
                // Parry succeeds. Enemy does no damage. Player gets an extra turn.
                // effects.SetTrigger("parrySucceed");
                enemyDmg = 0;

                showDialogue("- but you parried it back!");
                yield return new WaitForSeconds(0.05f * "- but you parried it back!".Length);

            }
            else
            {
                Debug.Log("parry fails");

                // Parry fails. Enemy gets an extra turn.
                //effects.SetTrigger("parryFail");
                dialogueTxt.color = new Color(255, 255, 255);
                state = BattleState.ENEMYTURN;
                StartCoroutine(EnemyTurn());
            }

            parryAttempt = false;
        }*/



        // Damage effect
        if (enemyDmg > 0) // If the attack does damage, play the hurt animation
        {
            battleSFX.PlayOneShot(isHurt);
            effects.SetTrigger("isHurt");
        }

        // Account for bulwarking
        if (guardCount > 0)
        {
            enemyDmg *= 0.75f;
            bool isDead = playerUnit.TakeDamage(enemyDmg); // Subtract from the player's health pool. HALF damage if guarding
            if (isDead)
            {
                state = BattleState.LOST;
                EndBattle();
            }
            else
            {
                state = BattleState.PLAYERTURN;
                PlayerTurn();

                // Reduce bulwark turn
                if (guardCount > 0)
                {
                    guardCount -= 1;
                    Debug.Log("Counting down. " + guardCount + " turns left for bulwarking");

                    // Remove bulwark UI if not guarding anymore
                    if (guardCount <= 0)
                    {
                        bulwarkFX.SetBool("isBulwarking", false);
                    }
                }
            }
        }
        else
        {
            bool isDead = playerUnit.TakeDamage(enemyDmg); // Subtract from the player's health pool
            if (isDead)
            {
                state = BattleState.LOST;
                EndBattle();
            }
            else
            {
                yield return new WaitForSeconds(1f);
                state = BattleState.PLAYERTURN;
                PlayerTurn();
            }
        }
    }

    public void onPlayerAtk() // When player chooses to fight
    {
        if (state != BattleState.PLAYERTURN)
        {
            return;
        }

        StartCoroutine(PlayerAttack());
    } 

    IEnumerator PlayerAttack()
    {
        if (atkUpCount > 0)
        {
            showDialogue("You stab with force deep into " + enemyUnit.name + ". It feels riveting.");
            yield return new WaitForSeconds((0.05f * ("You stab with force deep into " + enemyUnit.name + ". It feels riveting.").Length) + 1f);
        }
        else
        {
            showDialogue("You stab deep into " + enemyUnit.name + ".");
            yield return new WaitForSeconds((0.05f * ("You stab deep into " + enemyUnit.name + ".").Length) + 1f);
        }

        effects.SetTrigger("stab");
        battleSFX.PlayOneShot(stab);

        var playerActions = new Dictionary<string, object[]>()
        {
            {"stab", new object[]{5, "You stab into " } }
        };

        // Damage the enemy. Account for riveting
        if (atkUpCount > 0)
        {
            //Debug.Log("attacking with force");
            enemyUnit.TakeDamage(playerUnit.damage * 1.25f);
        }
        else
        {
            //Debug.Log("attacking normally");
            enemyUnit.TakeDamage(playerUnit.damage);
        }
        
        

        // Check if enemy is dead
        // Change state if yes
        if (enemyUnit.currentHP <= 0)
        {
            // End the battle
            state = BattleState.WON;
            EndBattle();
        }
        else
        {
            // Enemy's turn now
            state = BattleState.ENEMYTURN;
            StartCoroutine(EnemyTurn());
        }
    }

    void AttemptFlee()
    {
        Debug.Log("attempting to flee");
        float flee = enemy.GetComponent<Monster>().flee;

        if (UnityEngine.Random.Range(1, 100) >= flee)
        {
            // Player successfully flees           
            player.GetComponent<FirstPersonAIO>().playerCanMove = true;
            //enemy.GetComponent<NavMeshAgent>().isStopped = false;
            //Destroy(enemy);
            enemy.GetComponent<NavMeshAgent>().isStopped = false;
            rivetFX.SetBool("isRiveting", false);
            bulwarkFX.SetBool("isBulwarking", false);
            guardCount = 0;
            atkUpCount = 0;

            confrontScreen.SetActive(false);
            player.tag = "Player";

            jukebox.resumeAmbiance();
        }
        else
        {
            // Flee fails

            // Enemy's turn now
            state = BattleState.ENEMYTURN;
            StartCoroutine(EnemyTurn());

        }
    }

    void EndBattle()
    {
        // Player won the battle 
        if(state == BattleState.WON)
        {
            Debug.Log("you won!");
            Instantiate(enemyDeath, enemy.transform.position, Quaternion.identity);
            player.GetComponent<FirstPersonAIO>().playerCanMove = true;
            Destroy(enemy);

            // Player recovers some health
            playerUnit.Heal(2);

            // Add enemy to Enemies Defeated list
            GameObject.Find("ProgressManager").GetComponent<ProgressManager>().enemiesDefeated.Add(enemyUnit.name);
            
            // Play new ambiance song if more than half of enemies are defeated
            if (GameObject.Find("ProgressManager").GetComponent<ProgressManager>().enemiesDefeated.Count >= 3)
            {
                jukebox.ambianceTransition();
            }
            
        }
        // Pleyer lost the battle
        else if (state == BattleState.LOST)
        {
            enemy.GetComponent<NavMeshAgent>().isStopped = false;

            // Lose sequence
            sl.LoadLose();
        }

        // Revert all the status effects
        guardCount = 0;
        atkUpCount = 0;
        rivetFX.SetBool("isRiveting", false);
        bulwarkFX.SetBool("isBulwarking", false);

        jukebox.resumeAmbiance();

        confrontScreen.SetActive(false);
        player.tag = "Player";
    }

    // Call this function when you want to show animated text
    void showDialogue(string text)
    {
        StartCoroutine(AnimateText(text));
    }

    IEnumerator AnimateText(string s)
    {
        // Loops through the given string and prints it one character at a time
        string current = "";
        for(int i=0; i<=s.Length; i++)
        {
            current = s.Substring(0, i);
            dialogueTxt.text = current;
            yield return new WaitForSeconds(0.05f);
        }

    }

    private void ShowAttack(string action)
    {
        if (action == "bite")
        {
            effects.SetTrigger("bite");
        }
        else if (action == "blink1" || action == "blink2")
        {
            effects.SetTrigger("blink");
        }
        else if (action == "cry1" || action == "cry2" || action == "cry3")
        {
            effects.SetTrigger("cry");
        }
        else if (action == "dance")
        {
            effects.SetTrigger("dance");
        }
        else if (action == "hit")
        {
            effects.SetTrigger("hit");
        }
        else if (action == "mutate")
        {
            effects.SetTrigger("mutate");
        }

    }
}

For my original game No Body, developed in Unity Engine, I created a fighting system inspired by traditional Pokémon battles.

Previous
Previous

My Mini Pokédex

Next
Next

Automatic Texture ID Applier