Health.cs
using Sandbox;
public interface IPlayerHealthEvent : ISceneEvent<IPlayerHealthEvent>
{
void OnSpawned(GameObject player) { }
void OnTakeDamage( GameObject player, ref DamageInfo damage ) { }
void OnDied( GameObject player ) { }
/// <summary>
/// Fired when the player's camera has been destroyed
/// </summary>
void OnPostDeath(GameObject player) { }
}
public sealed class Health : Component, Component.IDamageable
{
[Property]
public int StartingHealth { get; set; } = 100;
[Property]
public int MaxHealth { get; set; } = 100;
[Property]
public SoundEvent Hurt { get; set; }
[Property]
public SoundEvent Death { get; set; }
public bool IsAlive => _currentHealth > 0;
public float CurrentHealth => _currentHealth;
private float _currentHealth;
protected override void OnEnabled()
{
base.OnEnabled();
_currentHealth = StartingHealth;
GetComponent<HUD>().Health = (int)_currentHealth;
IPlayerHealthEvent.Post( x => x.OnSpawned( GameObject ) );
}
void IDamageable.OnDamage( in DamageInfo damage )
{
if ( !IsAlive ) return;
// Log.Info( $"hurt for {damage}" );
_currentHealth -= damage.Damage;
if ( _currentHealth > MaxHealth ) _currentHealth = MaxHealth;
if ( _currentHealth <= 0 ) _currentHealth = 0;
var clonedDamage = damage;
Scene.RunEvent<IPlayerHealthEvent>( x => x.OnTakeDamage( GameObject, ref clonedDamage ) );
if ( _currentHealth == 0 )
{
Sound.Play( Death, WorldPosition ).Parent = GameObject;
Die(damage);
}
else
{
if (damage.Damage > 0)
{
Sound.Play( Hurt, WorldPosition ).Parent = GameObject;
}
}
}
void Die(DamageInfo finalDamage)
{
Scene.RunEvent<IPlayerHealthEvent>( x => x.OnDied( GameObject ) );
// Turn off various features of the player controller
var player = GetComponent<PlayerController>();
// Stop moving around
player.UseInputControls = false;
// Cancel current movement
player.WishVelocity = 0;
// Stop controlling the model's animations
player.Renderer = null;
// Clear all parameters on the model
var smr = player.GetComponentInChildren<SkinnedModelRenderer>();
smr.ClearParameters();
// Close the eyes and stop animating
smr.UseAnimGraph = false;
smr.Sequence.Name = "Eyes_Closed";
// Turn on our ragdoll component
var ragdoll = GameObject.Components.Get<ModelPhysics>( FindMode.EverythingInDescendants );
ragdoll.Enabled = true;
if (finalDamage.Tags.Has("explosion"))
{
foreach (var body in ragdoll.Bodies)
{
var rb = body.Component;
rb.ApplyForce( Game.Random.Angles().AsVector3().WithZ( 700f ) );
}
}
// Free it from the player object so it sticks around
ragdoll.GameObject.SetParent( null );
}
}