// We were hit! public IEnumerator GotHit(Vector3 hitDir, float hitForce, float stunTimeAfterStop, float stunAdd, int damage, int hurtOther, Transform attacker, bool guarded, Vector3 hitPos, bool hitRelative = true, CharacterAttacking charAttacking = null) { // If we are dead or successfully guarded the attack, we will get // out of here. if(Stats[0] <= 0 || guarded) { if(guarded) anim.SetTrigger("WasHit"); StopCoroutine("GotHit"); yield break; } // Call the ResetParameters() method on all of our character scripts // that have it. gameObject.SendMessage("ResetParameters"); // We subtract our defense stat from the damage received. int damageTaken = damage - Stats [3]; bool slammed = hitDir.y < -3 && !_charMotor.onGround; // Air slammed. if(!Stunned) { _stunAmount += stunAdd; // Add stun. // I found 8 was a good amount for being stunned with my setup now. // Decrease this if you want characters to be stunned easier. You will not // be stunned if about to die. if(_stunAmount > 8 && _charMotor.onGround && !slammed && hitDir.y < 6 && anim.GetInteger("HurtOther") < 3 && (Stats[0] - damageTaken > 0)) { // Stunned! hurtOther = 3; _stunAmount = 8; Stunned = true; if(myDizzyParticle == null) myDizzyParticle = Manager_Particle.instance.CreateParticle(myHead.position, ParticleTypes.Dizzy, 1); if(myDizzyParticle.transform.parent != myHead) myDizzyParticle.transform.parent = myHead; myDizzyParticle.transform.localPosition = Vector3.zero; _dizPartEmit = myDizzyParticle.GetComponent<ParticleEmitter>(); if (!_dizPartEmit) Debug.Log("NO PARTICLE EMITTER FOUND FOR DIZZY PARTICLE!"); if (!myDizzyParticle.activeSelf) myDizzyParticle.SetActive(true); else if(_dizPartEmit) _dizPartEmit.emit = true; } } else { // If we are already stunned and we get hit, it always will result // in a knock down. hurtOther = 2; // Knocked down // The hit power increases so we get pushed further than we normally // were going to. hitDir.y = Mathf.Clamp(hitDir.y + 3, 3, 6); hitDir.x *= 1.5f; hitDir.z *= 1.5f; Vulnerable = false; Stunned = false; _stunAmount = 0; // More damage taken too. damageTaken = Mathf.RoundToInt(damageTaken * 1.5f); // We no longer need our dizzy particle. if (myDizzyParticle && _dizPartEmit && _dizPartEmit.emit) _dizPartEmit.emit = false; } if(damageTaken < 1) damageTaken = 1; // Minimum of one damage. Manager_Particle.instance.Create3DNumber(damageTaken, hitPos, hitDir + Vector3.up, false); // Get our health after subtracting our damageTaken. int health = TakeDamage (damageTaken, Stats[1]); // Change our health bar slider stuff if below 50% of our max health. if(health < Stats[1] * 0.5f) { if(health > Stats[1] * 0.25f) healthFill.color = Color.yellow; else // Below 25% max health. { healthFill.color = Color.red; anim.SetLayerWeight(1, 1); // Our injured layer now activates. _charMotor.LowHealthSetup(true); // Make speed adjustments. } } if(!IsEnemy) { // Increase our Hits Taken count for grading. if(PlayerNumber == 1) Manager_Game.P1HitsTaken++; else if(PlayerNumber == 2) Manager_Game.P2HitsTaken++; else if(PlayerNumber == 3) Manager_Game.P3HitsTaken++; else if(PlayerNumber == 4) Manager_Game.P4HitsTaken++; } if(anim.GetInteger("HurtOther") == 4) // Is being held. { if(health < 1) { // If we died, we no longer need to be held. StopCoroutine("WasGrabbed"); anim.SetInteger("HurtOther", 0); if (myTransform.parent) // Simply ensure that we still have a parent. { myTransform.SendMessageUpwards("GrabBroken"); // Tell our grabber that we are no longer grabbed. myTransform.parent = null; } _charMotor.GrabbedSetup(false); } else { anim.SetBool ("IsHurt", true); anim.SetTrigger("WasHit"); // We were hit while grabbed so we get out of here since // nothing else is needed in this case. We just go into our grabbed hurt state. StopCoroutine("GotHit"); yield break; } } // In case this needs to be reset from being grabbed, we do // so here. myRigidbody.constraints = RigidbodyConstraints.FreezeRotation; Vector3 dir = -myTransform.forward; if(attacker != null) dir = (attacker.position - myTransform.position).normalized; if(hitRelative) // Default hit direction setup { if(attacker != null) { // We find this simply by taking our position - attacker's position Vector3 relDir = myTransform.position - attacker.position; hitDir.x = relDir.x; hitDir.z = relDir.z; } } // With Vector3.Dot here, -1 being we are dead center behind the // attacker who hit us. 1 being the attacker who hit us is dead // center in front. float direction = Vector3.Dot(dir, myTransform.forward); // We update all of our Animator parameters, saving the trigger "WasHit" // for last. It's best to save triggers for last so all of the // parameters can be set properly to determine which state to go into // next. anim.SetInteger ("HurtOther", hurtOther); anim.SetFloat ("HitDir", direction); anim.SetBool ("IsHurt", true); anim.SetInteger ("Health", health); anim.SetTrigger ("WasHit"); if(_myAudio) // If we have an audio source, which we should. { AudioClip voiceToUse = null; // Assign a audio clip to play based on what kind of damage // we took (hurtOther) if(health > 0) { if(hurtOther < 2) { // If we are lightly hit (hurtOther == 0) we won't always play a hurt voice. If we // are hit hard (hurtOther == 1), then we always will. if(voicesHit.Length > 0 && ((hurtOther == 0 && Random.value > 0.5f) || hurtOther == 1)) voiceToUse = voicesHit[Random.Range(0, voicesHit.Length)]; } else if(hurtOther == 2) { if(voicesHitDown.Length > 0) voiceToUse = voicesHitDown[Random.Range(0, voicesHitDown.Length)]; } } if(voiceToUse != null) // If we assigned one, play it. Manager_Audio.PlaySound(_myAudio, voiceToUse, false); } // If knocked down or dead, make sure we aren't vulnerable. if(hurtOther == 2 || health <= 0) { Vulnerable = false; } if(hitDir.y > 8) // Limit how high we can be hit up into the air. hitDir.y = 8; // Increase these by the hitForce given. hitDir.x *= hitForce; hitDir.z *= hitForce; myRigidbody.velocity = Vector3.zero; yield return new WaitForSeconds(0.08f); myRigidbody.velocity = hitDir; if(health > 0) { if (slammed) { while (!_charMotor.onGround) yield return new WaitForSeconds(0.0001f); // After reaching here, we have hit the ground after being slammed. // Amount of damage from slamming into ground will be roughly 60% // of total damage taken from the slam attack. damageTaken = Mathf.RoundToInt(damageTaken * 0.6f); health = TakeDamage(Mathf.RoundToInt(damageTaken), Stats[1]); Manager_Particle.instance.Create3DNumber(Mathf.RoundToInt(damageTaken), myTransform.position + Vector3.up, hitDir, false); if (health <= 0) // Died from slam. { // No more health to display healthFill.enabled = false; // Do the die coroutine instead now. StartCoroutine(Die(attacker, charAttacking, Vector3.up * Mathf.Abs(hitDir.y * 0.9f))); // We died so get out of here. StopCoroutine("GotHit"); yield break; } else { // A small extra bounce from the land. myRigidbody.velocity = new Vector3(myRigidbody.velocity.x, Mathf.Abs(hitDir.y * 0.6f), myRigidbody.velocity.z); } } else { if(hitDir.y > 3) yield return new WaitForSeconds(0.4f); } // If knocked down, wait until we have landed properly. while((slammed || hurtOther == 2) && (!_charMotor.onGround || myRigidbody.velocity.y > 0.2f)) { if(attacker) _charMotor.ManualRotate(hitDir, anim.GetFloat("HitDir") != -1, 5); yield return new WaitForSeconds(0.0001f); } // If we were knocked down, here is where we will reset our // attacker's combo if we were the character they started comboing on. if(hurtOther == 2 && attacker != null) { if(_charAttacking == null) charAttacking = attacker.GetComponent<CharacterAttacking>(); if(charAttacking.StartedComboEnemy == myTransform) charAttacking.ComboChange(false); if(IsEnemy) { if(Manager_Targeting.instance.playerTargets.Count > 1) { // If any other character just so happened to have us // as their started combo character. foreach(Transform player in Manager_Targeting.instance.playerTargets) { if(player != attacker) { CharacterAttacking pAttacking = player.GetComponent<CharacterAttacking>(); if(pAttacking.StartedComboEnemy == myTransform) pAttacking.ComboChange(false); } } } } } if (Stunned) // was stunned { while (_stunAmount > 0) { // Additional recovery with input or AI's attempt to recover. if (!AIOn) { if ((_playerInput.enabled && Input.GetButtonDown(_playerInput.InputGuard)) || (_playerInputMobile.enabled && _playerInputMobile.guardButtonPressed == 1)) _stunAmount -= 0.3f; } else { if (Random.value < _characterAI.evadeRatio) _stunAmount -= 0.02f; } _stunAmount -= 5 * Time.deltaTime; // Stun recover speed yield return 0; } // No longer dizzy/stunned. Stunned = false; _stunAmount = 0; if (myDizzyParticle && _dizPartEmit && _dizPartEmit.emit) _dizPartEmit.emit = false; } else { yield return new WaitForSeconds(stunTimeAfterStop); } // No longer hurt. Resetting this will bring us back into our idle // state. anim.SetBool("IsHurt", false); anim.SetInteger("HurtOther", 0); // Wait until we are in a NotBusy state again. while(!BaseStateInfo.IsTag("NotBusy") && anim.GetInteger("Health") > 0) yield return new WaitForSeconds(0.0001f); // After recovering and back in idle or falling, reset attacker's combo. if(hurtOther != 2 && attacker != null) { if(charAttacking == null) charAttacking = attacker.GetComponent<CharacterAttacking>(); if(charAttacking.StartedComboEnemy == myTransform) charAttacking.ComboChange(false); if(IsEnemy) { if(Manager_Targeting.instance.playerTargets.Count > 1) { foreach(Transform player in Manager_Targeting.instance.playerTargets) { if(player != attacker) { CharacterAttacking pAttacking = player.GetComponent<CharacterAttacking>(); if(pAttacking.StartedComboEnemy == myTransform) pAttacking.ComboChange(false); } } } } } if(AIOn) _characterAI.Agent.Resume(); // Have our Agent guide us again. // An attempt for the AI to change its targeted character to the // attacker who attacked them if they don't have them targeted already. if(AIOn) { if(_charAttacking.TargetedCharacter != attacker && Random.value > 0.4f) _characterAI.ChangeTarget(attacker); } // If we were knocked down, start the flashing setup while we aren't // vulnerable to show that. if(hurtOther == 2) { StartFlash(0.2f, 0.3f, 2, FlashType.Color_Change); } } if(anim.GetInteger("Health") < 1) { // No more health to display healthFill.enabled = false; // This is where all the Die-relating things happen. StartCoroutine(Die (attacker, charAttacking, hitDir)); } StopCoroutine ("GotHit"); }
IEnumerator Die(Transform attacker, CharacterAttacking charAttacking, Vector3 hitDir) { if(!IsEnemy) InputChange (false); _charMotor.GrabbedSetup(false); if(voiceDie) { if (deadPrefab == null) Manager_Audio.PlaySound(_myAudio, voiceDie, false); else AudioSource.PlayClipAtPoint(voiceDie, myTransform.position); } // Stop displaying our target cursor. Manager_Targeting.instance.DisableTargetCursors (PlayerNumber, _charAttacking); if (myDizzyParticle && _dizPartEmit && _dizPartEmit.emit) { _dizPartEmit.emit = false; Stunned = false; _stunAmount = 0; } gameObject.name += "Dead"; // Now Detection radius' won't find us. if(IsEnemy) { // This enemy is no longer a target. if(Manager_Targeting.instance.enemyTargets.Contains(this.transform)) Manager_Targeting.instance.enemyTargets.Remove(this.transform); } if(attacker != null) { // Setup for resetting attacker's combo and changing target for // anyone who has this character targeted since they are dead, // they shouldn't be targeted anymore. if(charAttacking == null) charAttacking = attacker.GetComponent<CharacterAttacking>(); if(charAttacking.StartedComboEnemy == myTransform) charAttacking.ComboChange(false); if(IsEnemy) { if(charAttacking.TargetedCharacter == myTransform) charAttacking.TargetChange(); if(Manager_Targeting.instance.playerTargets.Count > 1) { foreach(Transform player in Manager_Targeting.instance.playerTargets) { if(player != attacker) { CharacterAttacking pAttacking = player.GetComponent<CharacterAttacking>(); if(pAttacking.StartedComboEnemy == myTransform) pAttacking.ComboChange(false); if(pAttacking.TargetedCharacter == myTransform) pAttacking.TargetChange(); } } } } } // Additional height added from dying. hitDir.y = 3 + (hitDir.y * 2); if(IsEnemy) { // Get rid of our health bar and NavMeshAgent. Destroy (healthSlider.transform.parent.gameObject); Destroy (_characterAI.Agent.gameObject, 0.1f); _characterAI.enabled = false; // We are no longer needed in this list. Manager_Game.instance.enemiesAll.Remove(this.gameObject); } Transform myCamTarget = myTransform; if(deadPrefab != null) // A ragdoll is assigned. { // Create our ragdoll and give it velocity based on our hitDir. GameObject ragdoll = Instantiate(deadPrefab, myTransform.position, myTransform.rotation) as GameObject; Rigidbody ragRigid = ragdoll.GetComponentInChildren<Rigidbody>(); ragRigid.velocity = new Vector3(hitDir.x * 3, hitDir.y, hitDir.z * 3); if(IsEnemy) { // Our ragdoll is the dead enemy for us now. Manager_Game.instance.enemiesDead.Add(ragdoll); Destroy(this.gameObject); StopCoroutine("GotHit"); yield break; } else { // Make the cam switch its current target of this character with // the ragdoll. Use the one with the rigidbody on it. Camera_BEU.instance.ChangeTarget(myTransform, ragRigid.transform); myCamTarget = ragRigid.transform; // Make it so we can't be seen. foreach(SkinnedMeshRenderer skinMesh in mySkinnedMeshes) skinMesh.enabled = false; // We disable ourselves while the ragdoll is there. myRigidbody.isKinematic = true; GetComponent<Collider>().enabled = false; yield return new WaitForSeconds(4); if(playerLives > 1) // We can respawn. { // Change the cam back to us in place of our ragdoll. Camera_BEU.instance.ChangeTarget(ragRigid.transform, myTransform); Destroy (ragdoll); // No longer need the ragdoll. } } } else // Die using an animation. { if(IsEnemy && !Manager_Game.instance.enemiesDead.Contains(this.gameObject)) Manager_Game.instance.enemiesDead.Add(this.gameObject); } if(IsEnemy) _charAttacking.enabled = false; else { if(playerLives > 1) { if(deadPrefab == null) // Died using an animation. { yield return new WaitForSeconds(2.9f); // Dead time foreach(SkinnedMeshRenderer skinnedMesh in mySkinnedMeshes) skinnedMesh.enabled = false; yield return new WaitForSeconds(0.1f); } Respawn(); yield return new WaitForSeconds(1.5f); } else // No more lives left. { Manager_Game.instance.LiveChange(PlayerNumber, -1); // We are no longer an active player in the game... if(Manager_Targeting.instance.playerTargets.Contains(this.transform)) Manager_Targeting.instance.playerTargets.Remove(this.transform); Manager_Game.instance.allPlayerStatus.Remove(this); if(!Manager_Game.playersDefeated.Contains(this.gameObject)) Manager_Game.playersDefeated.Add(this.gameObject); // The Manager_UI will now not display our results at the end of // the stage, provided that another player makes it. if(PlayerNumber == 1) Manager_UI.ShowP1Results = false; else if(PlayerNumber == 2) Manager_UI.ShowP2Results = false; else if(PlayerNumber == 3) Manager_UI.ShowP3Results = false; else if(PlayerNumber == 4) Manager_UI.ShowP4Results = false; // The camera doesn't need to target us anymore. Camera_BEU.instance.targets.Remove(this.transform); if(Manager_Game.playersDefeated.Count == Manager_Game.NumberOfPlayers) { // Game Over! Manager_Game.instance.Invoke("GameOver", 1); } else // Other players remain. { // Wait until the battle ends. while(Manager_BattleZone.instance.InBattle) yield return 0; Camera_BEU.instance.RemoveTarget(myCamTarget); // Next is where we control an AI player who may still // be active in the game using our controls while keeping // their player number. foreach(Transform player in Manager_Targeting.instance.playerTargets) { if(player) { CharacterStatus cStatus = player.GetComponent<CharacterStatus>(); if(cStatus.AIOn) { if(player == Manager_Targeting.instance.playerTargets[0]) cStatus.InputChange(true, true, PlayerNumber); cStatus.SendMessage("FindLeadPlayer"); } } } // We will flash away and then be deactivated if we do not have a ragdoll if(!deadPrefab) StartFlash(0.2f, 0.6f, 3, FlashType.Renderer_Disable); } } } StopCoroutine ("Die"); }