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.