PlayerHealth.cs

Component attached to a player entity that manages health, death and respawn. It tracks MaxHealth and CurrentHealth, applies damage and healing, disables renderers/controllers on death, spawns a ragdoll impulse, updates a score manager, and restores components and position on respawn.

File Access
using Sandbox;
using System;
using System.Linq;

public sealed class PlayerHealth : Component
{
	[Property] public float MaxHealth { get; set; } = 100f;
	[Property] public float CurrentHealth { get; private set; } = 100f;

	public bool IsDead => CurrentHealth <= 0f;
	public float RespawnTimer { get; private set; } = 0f;
	public float RespawnDuration { get; set; } = 3f;

	private Vector3 _lastDamageDirection = Vector3.Zero;

	protected override void OnStart()
	{
		CurrentHealth = MaxHealth;
	}

	protected override void OnUpdate()
	{
		if ( IsDead )
		{
			RespawnTimer -= Time.Delta;
			if ( RespawnTimer <= 0f )
			{
				Respawn();
			}
		}
	}

	public void TakeDamage( float damage )
	{
		TakeDamage( damage, Vector3.Zero );
	}

	public void TakeDamage( float damage, Vector3 direction )
	{
		if ( IsDead ) return;

		_lastDamageDirection = direction;
		CurrentHealth = MathX.Clamp( CurrentHealth - damage, 0f, MaxHealth );

		if ( CurrentHealth <= 0f )
		{
			Die();
		}
	}

	public void Heal( float amount )
	{
		if ( IsDead ) return;
		CurrentHealth = MathX.Clamp( CurrentHealth + amount, 0f, MaxHealth );
	}

	private void Die()
	{
		RespawnTimer = RespawnDuration;

		if ( ScoreManager.Instance != null )
		{
			ScoreManager.Instance.AddDeath();
		}

		var ragdoll = Components.Get<PlayerRagdoll>();
		if ( ragdoll.IsValid() )
		{
			ragdoll.SpawnRagdoll( _lastDamageDirection * 2500f );
		}

		var skm = Components.Get<SkinnedModelRenderer>();
		if ( skm.IsValid() ) skm.Enabled = false;

		var mr = Components.Get<ModelRenderer>();
		if ( mr.IsValid() ) mr.Enabled = false;

		var cc = Components.Get<CharacterController>();
		if ( cc.IsValid() ) cc.Enabled = false;

		var rb = Components.Get<Rigidbody>();
		if ( rb.IsValid() ) rb.Enabled = false;

		CameraJuice.Instance?.Shake( 30f, 6f );

		Log.Info( "[PlayerHealth] Игрок погиб!" );
	}

	public void Respawn()
	{
		CurrentHealth = MaxHealth;

		var skm = Components.Get<SkinnedModelRenderer>();
		if ( skm.IsValid() ) skm.Enabled = true;

		var mr = Components.Get<ModelRenderer>();
		if ( mr.IsValid() ) mr.Enabled = true;

		var cc = Components.Get<CharacterController>();
		if ( cc.IsValid() ) cc.Enabled = true;

		var rb = Components.Get<Rigidbody>();
		if ( rb.IsValid() ) rb.Enabled = true;

		var spawnPoint = Scene.GetAllComponents<SpawnPoint>().FirstOrDefault();
		if ( spawnPoint.IsValid() )
		{
			Transform.World = spawnPoint.Transform.World;
		}
		else
		{
			Transform.LocalPosition = Vector3.Up * 50f;
		}

		Log.Info( "[PlayerHealth] Игрок возродился!" );
	}
}