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.