- Players can now die (finally) - Solo players have a single cheat death per scene (reviving automatically after the first death) - group players have revive mechanic, where a player faints when his health gets to 0, creating a revive circle around him, other players can stand on it to revive him. if after x seconds they don't get revived they bleed out and stay perma death until scene changes or all players die - Multiple VFX added using post processing for cheat death, fainting, reviving, and perma death events. - stopped players from moving and pressing keys when dead - enemies now change target if they try to attack a dead/fainted target.
281 lines
8.1 KiB
C#
281 lines
8.1 KiB
C#
using Photon.Pun;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
public class PlayerDeathManager : MonoBehaviour, IPunObservable
|
|
{
|
|
[SerializeField] private GameEvent onLocalPlayerCheatedDeath;
|
|
[SerializeField] private GameEvent onLocalPlayerFainted;
|
|
[SerializeField] private GameEvent onLocalPlayerPermaDeath;
|
|
[SerializeField] private GameEvent onLocalPlayerRevived;
|
|
[Space]
|
|
[SerializeField] private GameObject reviveProgressGO;
|
|
[SerializeField] private GameObject bleedOutProgressGO;
|
|
[SerializeField] private Image reviveProgressFill;
|
|
[SerializeField] private Image bleedOutProgressFill;
|
|
|
|
private bool hasUsedCheatDeath = false;
|
|
public bool HasUsedCheatDeath => hasUsedCheatDeath;
|
|
|
|
private bool isFainted = false;
|
|
public bool IsFainted => isFainted;
|
|
|
|
private float bleedOutTimer = 0f;
|
|
public float BleedOutTimer => bleedOutTimer;
|
|
|
|
private float reviveProgress = 0f;
|
|
public float ReviveProgress => reviveProgress;
|
|
|
|
PhotonView owner;
|
|
RiftPlayer ownerPlayer;
|
|
Health health;
|
|
SphereCollider reviveTrigger;
|
|
CharacterAnimatorController anim;
|
|
|
|
RiftPlayer possibleReviver;
|
|
public List<RiftPlayer> playersInsideReviveTrigger = new List<RiftPlayer>();
|
|
|
|
private void Awake()
|
|
{
|
|
owner = GetComponentInParent<PhotonView>();
|
|
ownerPlayer = GetComponentInParent<RiftPlayer>();
|
|
health = GetComponentInParent<Health>();
|
|
anim = ownerPlayer.GetComponentInChildren<CharacterAnimatorController>();
|
|
|
|
reviveTrigger = GetComponentInChildren<SphereCollider>();
|
|
reviveTrigger.transform.localScale = Vector3.one * GameConstants.CharacterBalancing.ReviveTriggerRadius * 2;
|
|
reviveTrigger.isTrigger = true;
|
|
reviveTrigger.gameObject.SetActive(false);
|
|
|
|
reviveProgressGO.SetActive(false);
|
|
bleedOutProgressGO.SetActive(false);
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
if (!owner.IsMine) return;
|
|
|
|
health.onDeath.AddListener(OnDeath);
|
|
}
|
|
|
|
private void OnDeath()
|
|
{
|
|
if (PhotonNetwork.CurrentRoom.PlayerCount <= 1)
|
|
{
|
|
HandleSoloDeath();
|
|
}
|
|
else
|
|
{
|
|
HandleGroupDeath();
|
|
}
|
|
}
|
|
|
|
|
|
private void HandleSoloDeath()
|
|
{
|
|
if (!hasUsedCheatDeath)
|
|
{
|
|
CheatDeath();
|
|
}
|
|
else
|
|
{
|
|
Die();
|
|
}
|
|
}
|
|
|
|
private void HandleGroupDeath()
|
|
{
|
|
EnterFaintedState();
|
|
}
|
|
|
|
private void CheatDeath()
|
|
{
|
|
hasUsedCheatDeath = true;
|
|
health.SetCurrentValue(health.GetMaxValue() * GameConstants.CharacterBalancing.SoloCheatDeathHealthPercent);
|
|
StartCoroutine(SoloInvulnerability());
|
|
|
|
if (!owner.IsMine) return;
|
|
onLocalPlayerCheatedDeath.Raise();
|
|
}
|
|
|
|
private IEnumerator SoloInvulnerability()
|
|
{
|
|
// Implement invulnerability logic
|
|
health.SetInvulnerabilityState(true);
|
|
yield return new WaitForSeconds(GameConstants.CharacterBalancing.SoloCheatDeathInvulnerabilityDuration);
|
|
// Remove invulnerability
|
|
health.SetInvulnerabilityState(false);
|
|
}
|
|
|
|
private void EnterFaintedState()
|
|
{
|
|
isFainted = true;
|
|
reviveTrigger.gameObject.SetActive(true);
|
|
// Disable player movement and abilities
|
|
// Change player visuals to "fainted" state
|
|
health.SetIsDeadState(true);
|
|
StartCoroutine(BleedOutTimerCoroutine());
|
|
|
|
if (!owner.IsMine) return;
|
|
|
|
reviveProgressGO.SetActive(true);
|
|
bleedOutProgressGO.SetActive(true);
|
|
|
|
anim.OnDeath();
|
|
onLocalPlayerFainted.Raise();
|
|
}
|
|
|
|
private IEnumerator BleedOutTimerCoroutine()
|
|
{
|
|
bleedOutTimer = GameConstants.CharacterBalancing.GroupBleedOutDuration;
|
|
|
|
bleedOutProgressFill.fillAmount = bleedOutTimer / GameConstants.CharacterBalancing.GroupBleedOutDuration;
|
|
|
|
while (bleedOutTimer > 0 && isFainted)
|
|
{
|
|
bleedOutTimer -= Time.deltaTime;
|
|
bleedOutProgressFill.fillAmount = bleedOutTimer / GameConstants.CharacterBalancing.GroupBleedOutDuration;
|
|
yield return null;
|
|
}
|
|
|
|
if (isFainted)
|
|
{
|
|
Die();
|
|
}
|
|
}
|
|
|
|
private void OnTriggerEnter(Collider other)
|
|
{
|
|
if (!isFainted) return;
|
|
|
|
possibleReviver = other.GetComponentInParent<RiftPlayer>();
|
|
if (possibleReviver == null) return;
|
|
|
|
if (possibleReviver == ownerPlayer) return;
|
|
|
|
if (playersInsideReviveTrigger.Contains(possibleReviver)) return;
|
|
|
|
Debug.Log($"R: added {possibleReviver.name} as a reviver");
|
|
playersInsideReviveTrigger.Add(possibleReviver);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!owner.IsMine)
|
|
{
|
|
reviveProgressGO.SetActive(reviveTrigger.gameObject.activeSelf);
|
|
bleedOutProgressGO.SetActive(reviveTrigger.gameObject.activeSelf);
|
|
|
|
reviveProgressFill.fillAmount = reviveProgress / GameConstants.CharacterBalancing.ReviveTime;
|
|
bleedOutProgressFill.fillAmount = bleedOutTimer / GameConstants.CharacterBalancing.GroupBleedOutDuration;
|
|
|
|
return;
|
|
}
|
|
|
|
if (!isFainted) return;
|
|
|
|
if (playersInsideReviveTrigger.Count > 0)
|
|
{
|
|
reviveProgress += GameConstants.CharacterBalancing.ReviveSpeed * playersInsideReviveTrigger.Count * Time.deltaTime;
|
|
reviveProgressFill.fillAmount = reviveProgress / GameConstants.CharacterBalancing.ReviveTime;
|
|
if (reviveProgress >= GameConstants.CharacterBalancing.ReviveTime)
|
|
{
|
|
Revive();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnTriggerExit(Collider other)
|
|
{
|
|
if (!isFainted) return;
|
|
|
|
possibleReviver = other.GetComponentInParent<RiftPlayer>();
|
|
if (possibleReviver == null) return;
|
|
|
|
if (possibleReviver == ownerPlayer) return;
|
|
|
|
if (!playersInsideReviveTrigger.Contains(possibleReviver)) return;
|
|
|
|
Debug.Log($"R: removed {possibleReviver.name} from revivers pool");
|
|
playersInsideReviveTrigger.Remove(possibleReviver);
|
|
}
|
|
|
|
private void Revive()
|
|
{
|
|
isFainted = false;
|
|
reviveTrigger.gameObject.SetActive(false);
|
|
reviveProgress = 0;
|
|
|
|
health.SetIsDeadState(false);
|
|
health.SetCurrentValue(health.GetMaxValue() * GameConstants.CharacterBalancing.ReviveHealthPercent);
|
|
|
|
// Re-enable player movement and abilities
|
|
// Restore normal player visuals
|
|
|
|
if (!owner.IsMine) return;
|
|
|
|
reviveProgressGO.SetActive(false);
|
|
bleedOutProgressGO.SetActive(false);
|
|
|
|
anim.OnRevived();
|
|
onLocalPlayerRevived.Raise();
|
|
}
|
|
|
|
private void Die()
|
|
{
|
|
health.SetIsDeadState(true);
|
|
|
|
// Implement full death logic (respawn, etc.)
|
|
if (PhotonNetwork.CurrentRoom.PlayerCount <= 1)
|
|
{
|
|
//Player perma death on the job, wait X to show info, then PhotonNetwork.LoadLevel(RiftHuntersInn)
|
|
StartCoroutine(Return());
|
|
}
|
|
else
|
|
{
|
|
//if players alive > 0
|
|
//wait motherfucker
|
|
//else
|
|
//all dead, host load level
|
|
}
|
|
|
|
if (!owner.IsMine) return;
|
|
|
|
if(!isFainted)
|
|
anim.OnDeath();
|
|
|
|
reviveProgressGO.SetActive(false);
|
|
bleedOutProgressGO.SetActive(false);
|
|
|
|
onLocalPlayerPermaDeath.Raise();
|
|
}
|
|
|
|
IEnumerator Return()
|
|
{
|
|
yield return new WaitForSeconds(GameConstants.GameBalancing.PermaDeathInfoTime);
|
|
|
|
|
|
if (PhotonNetwork.IsMasterClient) PhotonNetwork.LoadLevel("4-RiftHuntersInn");
|
|
}
|
|
|
|
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
|
|
{
|
|
if(stream.IsWriting)
|
|
{
|
|
stream.SendNext(isFainted);
|
|
stream.SendNext(reviveTrigger.gameObject.activeSelf);
|
|
stream.SendNext(reviveProgress);
|
|
stream.SendNext(bleedOutTimer);
|
|
}
|
|
else if(stream.IsReading)
|
|
{
|
|
isFainted = (bool)stream.ReceiveNext();
|
|
reviveTrigger.gameObject.SetActive((bool)stream.ReceiveNext());
|
|
reviveProgress = (float)stream.ReceiveNext();
|
|
bleedOutTimer = (float)stream.ReceiveNext();
|
|
}
|
|
}
|
|
}
|