using System.Collections; using UnityEngine; namespace WarriorAnims { public class WarriorController:SuperStateMachine { #region Components [Header("Components")] public Warrior warrior; public GameObject target; public GameObject weapon; private Rigidbody rb; [HideInInspector] public SuperCharacterController superCharacterController; [HideInInspector] public WarriorMovementController warriorMovementController; [HideInInspector] public WarriorInputController warriorInputController; [HideInInspector] public WarriorInputSystemController warriorInputSystemController; [HideInInspector] public WarriorTiming warriorTiming; [HideInInspector] public Animator animator; [HideInInspector] public WarriorIKHands ikHands; #endregion #region Inputs // Inputs. [HideInInspector] public bool inputAiming; [HideInInspector] public float inputAimVertical = 0; [HideInInspector] public float inputAimHorizontal = 0; [HideInInspector] public bool inputAttack; [HideInInspector] public bool inputAttackMove; [HideInInspector] public bool inputAttackRanged; [HideInInspector] public bool inputAttackSpecial; [HideInInspector] public bool inputBlock; [HideInInspector] public bool inputDeath; [HideInInspector] public bool inputJump; [HideInInspector] public bool inputLightHit; [HideInInspector] public bool inputRoll; [HideInInspector] public bool inputSheath; [HideInInspector] public bool inputTarget; [HideInInspector] public float inputVertical = 0; [HideInInspector] public float inputHorizontal = 0; [HideInInspector] public Vector3 moveInput; [HideInInspector] public Vector2 aimInput; private bool useInputSystem; public bool allowedInput { get { return _allowedInput; } } private bool _allowedInput = true; #endregion #region Variables // Variables. [HideInInspector] public bool isMoving; [HideInInspector] public bool isDead = false; [HideInInspector] public bool isBlocking = false; [HideInInspector] public bool isTargeting = false; [HideInInspector] public bool jumpAttack; [HideInInspector] public bool sheathed; [HideInInspector] public bool waitingOnWeapons = true; [HideInInspector] public bool useRootMotion = false; private bool canChain; private int attack; private int specialAttack; public bool canAction { get { return _canAction; } } private bool _canAction = true; public bool canBlock { get { return _canBlock && !sheathed; } } private bool _canBlock = true; public bool canMove { get { return _canMove; } } private bool _canMove = true; public bool canJump { get { return _canJump && !sheathed && !isDead; } } private bool _canJump = true; public bool canDoubleJump { get { return _canDoubleJump && !sheathed; } } private bool _canDoubleJump = false; public bool canRunAttack { get { return warrior == Warrior.Archer || warrior == Warrior.Crossbow || warrior == Warrior.TwoHanded; } } // Animation speed control. (doesn't affect lock timing) public float animationSpeed = 1; public Coroutine co; #endregion #region Initialization private void Awake() { // Get SuperCharacterController. superCharacterController = GetComponent(); // Get Movement Controller. warriorMovementController = GetComponent(); // Add Timing Controllers. warriorTiming = gameObject.AddComponent(); warriorTiming.warriorController = this; // Add IKHands. ikHands = GetComponentInChildren(); if (ikHands != null) { if (warrior == Warrior.TwoHanded || warrior == Warrior.Hammer || warrior == Warrior.Crossbow || warrior == Warrior.Spearman) { ikHands.canBeUsed = true; ikHands.BlendIK(true, 0, 0.25f); } } // Setup Animator, add AnimationEvents script. animator = GetComponentInChildren(); if (animator == null) { Debug.LogError("ERROR: There is no Animator component for character."); Debug.Break(); } else { animator.gameObject.AddComponent(); animator.GetComponent().warriorController = this; animator.gameObject.AddComponent(); animator.GetComponent().animator = animator; animator.GetComponent().warriorController = this; animator.updateMode = AnimatorUpdateMode.AnimatePhysics; animator.cullingMode = AnimatorCullingMode.CullUpdateTransforms; } // Determine input source. warriorInputController = GetComponent(); if (warriorInputController != null) { useInputSystem = false; } else { warriorInputSystemController = GetComponent(); if (warriorInputSystemController != null) { useInputSystem = true; } else { Debug.LogError("No inputs!"); } } // Setup Rigidbody. rb = GetComponent(); if (rb != null) { rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; } currentState = WarriorState.Idle; SwitchCollisionOn(); } #endregion #region Input /// /// Takes input from either WarriorInputController or WarriorInputSystemController. /// private void GetInput() { if (allowedInput) { // Use input from WarriorInputController / Input Manager. if (!useInputSystem) { if (warriorInputController != null) { inputAiming = warriorInputController.inputAiming; aimInput = warriorInputController.aimInput; inputAttack = warriorInputController.inputAttack; inputAttackMove = warriorInputController.inputAttackMove; inputAttackRanged = warriorInputController.inputAttackRanged; inputAttackSpecial = warriorInputController.inputAttackSpecial; inputBlock = warriorInputController.hasBlockInput; inputDeath = warriorInputController.inputDeath; inputJump = warriorInputController.inputJump; inputLightHit = warriorInputController.inputLightHit; inputTarget = warriorInputController.hasTargetInput; moveInput = warriorInputController.moveInput; } } // Use input from WarriorInputSystemController / Warrior Input Actions. else { if (warriorInputSystemController != null) { inputAiming = warriorInputSystemController.inputAiming; aimInput = warriorInputSystemController.aimInput; inputAttack = warriorInputSystemController.inputAttack; inputAttackMove = warriorInputSystemController.inputAttackMove; inputAttackRanged = warriorInputSystemController.inputAttackRanged; inputAttackSpecial = warriorInputSystemController.inputAttackSpecial; inputBlock = warriorInputSystemController.inputBlock; inputDeath = warriorInputSystemController.inputDeath; inputJump = warriorInputSystemController.inputJump; inputLightHit = warriorInputSystemController.inputLightHit; inputTarget = warriorInputSystemController.inputTarget; moveInput = warriorInputSystemController.moveInput; } } } } /// /// Checks move input and returns if active. /// public bool HasMoveInput() { return moveInput != Vector3.zero; } /// /// Checks aim input and returns if active. /// public bool HasAimInput() { return aimInput != Vector2.zero; } /// /// Checks block input and returns if true/false. /// public bool HasBlockInput() { return inputBlock; } /// /// Checks block input and returns if true/false. /// public bool HasTargetInput() { return inputTarget; } /// /// Shuts off input from WarriorInputController or WarriorInputSystemController. GUI still enabled. /// public void AllowInput(bool b) { _allowedInput = b; } #endregion #region Updates private void Update() { GetInput(); // Character is on ground. if (MaintainingGround()) { Attacking(); if (canAction) { Blocking(); if (inputLightHit) { GetHit(); } if (!isBlocking && !sheathed) { Targeting(); } } DeathRevive(); } // Character is in air. else { if (inputAttack) { JumpAttack(); } } UpdateAnimationSpeed(); } /// /// Updates the Animator with the animation speed multiplier. /// private void UpdateAnimationSpeed() { SetAnimatorFloat("AnimationSpeed", animationSpeed); } #endregion #region Combat /// /// Warrior jumps. /// public void Jump() { // Turn IK off for Crossbow Warrior. if (warrior == Warrior.Crossbow && ikHands != null) { ikHands.SetIKOff(); } } /// /// Warrior lands. /// public void Land() { LockJump(false); LockDoubleJump(true); // Turn IK on for Crossbow Warrior. if (warrior == Warrior.Crossbow && ikHands != null) { ikHands.BlendIK(true, 0.5f, 0.25f); } // Lock Warrior if JumpAttacked. if (jumpAttack) { Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, "jumpattack")); } } /// /// The different attack types. /// private void Attacking() { if (canAction) { if (inputAttack) { // Running attack. if (isMoving && canRunAttack) { RunningAttack(1); } else { AttackChain(); } } if (inputAttackMove) { MoveAttack(1); } if (inputAttackRanged) { RangeAttack(1); } if (inputAttackSpecial) { SpecialAttack(1); } } // Running Attack. if (inputAttack && isMoving && canMove) { RunningAttack(1); } // Chain Attack. if (inputAttack && canChain) { AttackChain(); } } /// /// Regular attack, cannot be chained. /// public void Attack(int attackNumber) { if (canAction) { Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, ("attack" + attackNumber.ToString()))); SetAnimatorInt("Action", attackNumber); SetAnimatorTrigger(AnimatorTrigger.AttackTrigger); if (warrior == Warrior.Spearman && attackNumber == 4 && ikHands != null) { ikHands.SetIKPause(warriorTiming.TimingLock(warrior, "attack4")); } if (warrior == Warrior.Spearman && attackNumber == 5 && ikHands != null) { ikHands.SetIKPause(warriorTiming.TimingLock(warrior, "attack5")); } } } /// /// 3 hit combo attack chain. /// public void AttackChain() { // If charater is not in air, do regular attack. if (attack == 0) { StartCoroutine(_Attack1()); } // If within chain time. else if (canChain) { if (warrior != Warrior.Archer) { if (attack == 1) { StartCoroutine(_Attack2()); } else if (attack == 2) { StartCoroutine(_Attack3()); } } } } /// /// First attack in the AttackChain. /// private IEnumerator _Attack1() { StopAllCoroutines(); SetAnimatorInt("Action", 1); SetAnimatorTrigger(AnimatorTrigger.AttackTrigger); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, "attack1")); ChainWindow((warriorTiming.TimingChain(warrior, "attack1start")), (warriorTiming.TimingChain(warrior, "attack1end"))); attack = 1; yield return null; } /// /// Second attack in the AttackChain. /// private IEnumerator _Attack2() { StopAllCoroutines(); canChain = false; SetAnimatorInt("Action", 2); SetAnimatorTrigger(AnimatorTrigger.AttackTrigger); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, "attack2")); ChainWindow((warriorTiming.TimingChain(warrior, "attack2start")), (warriorTiming.TimingChain(warrior, "attack2end"))); attack = 2; yield return null; } /// /// Third and final attack in the AttackChain. /// private IEnumerator _Attack3() { StopAllCoroutines(); SetAnimatorInt("Action", 3); SetAnimatorTrigger(AnimatorTrigger.AttackTrigger); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, "attack3")); if (warrior == Warrior.Hammer || warrior == Warrior.Spearman || warrior == Warrior.TwoHanded && ikHands != null) { ikHands.SetIKPause(warriorTiming.TimingLock(warrior, "attack3")); } // Reset attack chain. attack = 0; canChain = false; yield return null; } /// /// Attack while moving. Uses Upperbody layer mask and animation layer. /// public void RunningAttack(int attackNumber) { if (warrior == Warrior.Archer || warrior == Warrior.TwoHanded || warrior == Warrior.Crossbow) { SetAnimatorInt("Action", 1); SetAnimatorTrigger(AnimatorTrigger.AttackTrigger); } } /// /// Attack while in the air. Slams down to the ground. /// public void JumpAttack() { Debug.Log("JumpAttack"); if (warrior == Warrior.Karate || warrior == Warrior.Brute || warrior == Warrior.Hammer || warrior == Warrior.Spearman || warrior == Warrior.Swordsman || warrior == Warrior.TwoHanded || warrior == Warrior.Crossbow || warrior == Warrior.Mage) { jumpAttack = true; warriorMovementController.dropping = true; } } /// /// Special attack, or powerup buff. /// public void SpecialAttack(int attackNumber) { specialAttack = attackNumber; SetAnimatorInt("Action", attackNumber); SetAnimatorTrigger(AnimatorTrigger.AttackSpecialTrigger); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, ("special" + attackNumber.ToString()))); if (warrior == Warrior.Crossbow || warrior == Warrior.Hammer && ikHands != null) { ikHands.SetIKPause(warriorTiming.TimingLock(warrior, "special" + attackNumber.ToString())); } } /// /// Attack that moves the Warrior forward. /// public void MoveAttack(int attackNumber) { specialAttack = attackNumber; SetAnimatorInt("Action", attackNumber); SetAnimatorTrigger(AnimatorTrigger.AttackMoveTrigger); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, ("move" + attackNumber.ToString()))); if (warrior == Warrior.Hammer && ikHands != null) { ikHands.SetIKPause(warriorTiming.TimingLock(warrior, "move" + attackNumber.ToString())); } } /// /// Ranged attack. /// public void RangeAttack(int attackNumber) { specialAttack = attackNumber; SetAnimatorInt("Action", attackNumber); SetAnimatorTrigger(AnimatorTrigger.AttackRanged); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, ("range" + attackNumber.ToString()))); if (warrior == Warrior.TwoHanded) { StartCoroutine(_TwoHandedRangeAttack()); } } /// /// Throw sword attack for 2Handed Warrior. /// private IEnumerator _TwoHandedRangeAttack() { yield return new WaitForSeconds(0.7f); animator.transform.GetChild(3).gameObject.SetActive(true); HideShowWeapons(false); yield return new WaitForSeconds(1.325f); animator.transform.GetChild(3).gameObject.SetActive(false); HideShowWeapons(true); } /// /// Puts the Warrior in blocking state. /// public void Blocking() { if (canBlock) { if (!isBlocking) { if (HasBlockInput()) { isBlocking = true; _canMove = false; SetAnimatorBool("Blocking", true); SetAnimatorTrigger(AnimatorTrigger.BlockTrigger); if (warrior == Warrior.Crossbow && ikHands != null) { ikHands.BlendIK(false, 0, 0.1f); } } } else { if (!HasBlockInput()) { isBlocking = false; _canMove = true; SetAnimatorBool("Blocking", false); if (warrior == Warrior.Crossbow && ikHands != null) { ikHands.BlendIK(true, 0, 0.1f); } } } } } /// /// Knocks the Warrior back while in Blocking state. /// public void BlockBreak() { SetAnimatorTrigger(AnimatorTrigger.BlockBreakTrigger); Lock(true, true, true, true, 0, 1f); if (warrior == Warrior.TwoHanded && ikHands != null) { ikHands.SetIKPause(0.9f); } } /// /// Character will strafe around target. /// private void Targeting() { isTargeting = HasTargetInput(); SetAnimatorBool("Targeting", HasTargetInput()); } /// /// Character takes a light hit. /// public void GetHit() { SetAnimatorInt("Action", 1); SetAnimatorTrigger(AnimatorTrigger.LightHitTrigger); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, ("lighthit1".ToString()))); } /// /// Kills the Warrior, or if already dead, revives. If blocking, BlockBreak. /// private void DeathRevive() { // Death/Revive. if (inputDeath) { if (!isDead) { if (isBlocking) { BlockBreak(); } else { Death(); } } else { Revive(); } } } /// /// Kills the Warrior. /// public void Death() { SetAnimatorTrigger(AnimatorTrigger.DeathTrigger); Lock(true, true, true, false, 0.1f, 0f); if (warrior == Warrior.Crossbow || warrior == Warrior.TwoHanded && ikHands != null) { ikHands.SetIKOff(); } isDead = true; } /// /// Revives the Warrior from Death. /// public void Revive() { SetAnimatorTrigger(AnimatorTrigger.ReviveTrigger); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, "revive")); isDead = false; if (warrior == Warrior.Crossbow || warrior == Warrior.TwoHanded && ikHands != null) { ikHands.BlendIK(true, 1f, 0.25f); } } /// /// Character Dash. /// /// Forward. /// Right. /// Backward. /// Left. public void Dash(int dash) { // Knight has 2 sets of Dashes. if (dash < 0) { Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, "dash2")); } else { Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, "dash")); } SetAnimatorInt("Action", dash); SetAnimatorTrigger(AnimatorTrigger.DashTrigger); } #endregion #region Locks /// /// Lock character movement and/or action, on a delay for a set time. /// /// If set to true lock movement. /// If set to true lock action. /// If set to true timed. /// Delay time. /// Lock time. public void Lock(bool lockMovement, bool lockAction, bool lockJump, bool timed, float delayTime, float lockTime) { if (co != null) { StopCoroutine(co); } co = StartCoroutine(_Lock(lockMovement, lockAction, lockJump, timed, delayTime, lockTime)); } //Timed -1 = infinite, 0 = no, 1 = yes. public IEnumerator _Lock(bool lockMovement, bool lockAction, bool lockJump, bool timed, float delayTime, float lockTime) { if (delayTime > 0) { yield return new WaitForSeconds(delayTime); } if (lockMovement) { LockMove(true); } if (lockAction) { LockAction(true); } if (lockJump) { LockJump(true); } if (timed) { if (lockTime > 0) { yield return new WaitForSeconds(lockTime); UnLock(lockMovement, lockAction, lockJump); } } } /// /// Keep character from moving and use or diable Rootmotion. /// public void LockMove(bool b) { _canMove = !b; SetAnimatorRootMotion(b); if (b) { SetAnimatorBool("Moving", false); moveInput = Vector3.zero; } } /// /// Keep character from doing actions. /// public void LockAction(bool b) { _canAction = !b; } /// /// Keep character from blocking. /// public void LockBlock(bool b) { _canBlock = !b; } /// /// Keep character from jumping. /// public void LockJump(bool b) { _canJump = !b; } /// /// Keep character from double jumping. /// public void LockDoubleJump(bool b) { _canDoubleJump = !b; } /// /// Let character move and act again. /// private void UnLock(bool movement, bool actions, bool jump) { if (movement) { LockMove(false); } if (actions) { LockAction(false); } if (jump) { LockJump(false); } } #endregion #region Misc /// /// Returns whether the character is near the ground. /// public bool AcquiringGround() { return superCharacterController.currentGround.IsGrounded(false, 0.01f); } /// /// Returns whether the character is on the ground. /// public bool MaintainingGround() { return superCharacterController.currentGround.IsGrounded(true, 0.5f); } /// /// Locks the movement of the Warrior and turns off its collision. /// public void SwitchCollisionOff() { _canMove = false; superCharacterController.enabled = false; SetAnimatorRootMotion(true); if (rb != null) { rb.isKinematic = false; } } /// /// Unlocks the movement of the Warrior and turns on its collision. /// public void SwitchCollisionOn() { _canMove = true; superCharacterController.enabled = true; SetAnimatorRootMotion(false); if (rb != null) { rb.isKinematic = true; } } /// /// Set Animator Trigger. /// public void SetAnimatorTrigger(AnimatorTrigger trigger) { Debug.Log("SetAnimatorTrigger: " + trigger + " - " + ( int )trigger); animator.SetInteger("TriggerNumber", ( int )trigger); animator.SetTrigger("Trigger"); } /// /// Set Animator Bool. /// public void SetAnimatorBool(string name, bool b) { animator.SetBool(name, b); } /// /// Set Animator float. /// public void SetAnimatorFloat(string name, float f) { animator.SetFloat(name, f); } /// /// Set Animator ingeter. /// public void SetAnimatorInt(string name, int i) { animator.SetInteger(name, i); } /// /// Set Animator to use root motion or not. /// public void SetAnimatorRootMotion(bool b) { useRootMotion = b; } /// /// Allows Attack chaining to occur within a set timeframe. /// How long to wait to start the window. /// How long the window stays open. /// public void ChainWindow(float timeToWindow, float chainLength) { StopCoroutine("_ChainWindow"); StartCoroutine(_ChainWindow(timeToWindow, chainLength)); } public IEnumerator _ChainWindow(float timeToWindow, float chainLength) { yield return new WaitForSeconds(timeToWindow); canChain = true; yield return new WaitForSeconds(chainLength); canChain = false; attack = 0; } /// /// Hide and Unhide the Warrior's weapon. /// public void SheathWeapons() { bool hideshow; if (sheathed) { sheathed = false; SetAnimatorBool("Weapons", true); SetAnimatorInt("Action", 2); SetAnimatorTrigger(AnimatorTrigger.WeaponSwitchTrigger); hideshow = true; if (warrior == Warrior.TwoHanded || warrior == Warrior.Hammer || warrior == Warrior.Crossbow || warrior == Warrior.Spearman && ikHands != null) { ikHands.BlendIK(true, 0.75f, 0.5f); } } else { sheathed = true; SetAnimatorBool("Weapons", false); SetAnimatorInt("Action", 1); SetAnimatorTrigger(AnimatorTrigger.WeaponSwitchTrigger); hideshow = false; if (warrior == Warrior.TwoHanded || warrior == Warrior.Hammer || warrior == Warrior.Crossbow || warrior == Warrior.Spearman & ikHands != null) { ikHands.BlendIK(false, 0f, 0.25f); } } StartCoroutine(_HideShowWeapons(hideshow)); Lock(true, true, true, true, 0, warriorTiming.TimingLock(warrior, "sheath")); } /// /// Waits for animation's Animation Event to trigger WArriorAnimatorEvent.cs's WeaponSwitch method. /// public IEnumerator _HideShowWeapons(bool hideshow) { while (waitingOnWeapons) { yield return null; } HideShowWeapons(hideshow); } /// /// Hide and Unhide the Warrior's weapon. /// How long the window stays open. /// private void HideShowWeapons(bool showhide) { if (weapon != null) { weapon.gameObject.SetActive(showhide); } waitingOnWeapons = true; } /// /// Prints out all the WarriorController variables. /// public void ControllerDebug() { Debug.Log("CONTROLLER SETTINGS---------------------------"); Debug.Log($"useInputSystem:{useInputSystem} allowedInput:{allowedInput} isMoving:{isMoving} " + $"isDead:{isDead} isBlocking:{isBlocking} isTargeting:{isTargeting} " + $"jumpAttack:{jumpAttack} sheathed:{sheathed} waitingOnWeapons:{waitingOnWeapons} " + $"useRootMotion:{useRootMotion} canChain:{canChain} attack:{attack} " + $"specialAttack:{specialAttack} canAction:{canAction} canDoubleJump:{canDoubleJump} " + $"canMove:{canMove} canJump:{canJump} attack:{attack} " + $"animationSpeed:{animationSpeed}"); } /// /// Prints out all the Animator parameters. /// public void AnimatorDebug() { Debug.Log("ANIMATOR SETTINGS---------------------------"); Debug.Log($"Moving:{animator.GetBool("Moving")} Targeting:{animator.GetBool("Targeting")} Stunned:{animator.GetBool("Stunned")} " + $"Blocking:{animator.GetBool("Blocking")} Weapons:{animator.GetBool("Weapons")} Jumping:{animator.GetInteger("Jumping")} " + $"Action:{animator.GetInteger("Action")} TriggerNumber:{animator.GetInteger("TriggerNumber")} Velocity X:{animator.GetFloat("Velocity X")} " + $"Velocity Z:{animator.GetFloat("Velocity Z")}"); } #endregion } }