Player/PlayerFallDamage.cs
/// <summary>
/// Apply fall damage to the player
/// </summary>
public class PlayerFallDamage : Component, IPlayerEvent
{
	[RequireComponent] public Player Player { get; set; }

	/// <summary>
	/// Falling over this distance is considered a damaging fall
	/// </summary>
	[Property] public float MinimumFallDistance { get; set; } = 200;

	/// <summary>
	/// If you fall this distance it's death
	/// </summary>
	[Property] public float DeathFallDistance { get; set; } = 800;

	/// <summary>
	/// Multiply damage amount by this much
	/// </summary>
	[Property] public float DamageMultiplier { get; set; } = 1.0f;

	/// <summary>
	/// Fall damage sound
	/// </summary>
	[Property] public SoundEvent FallSound { get; set; }

	[Rpc.Owner]
	private void PlayFallSound()
	{
		GameObject.PlaySound( FallSound );
	}

	void IPlayerEvent.OnLand( float distance, Vector3 velocity )
	{
		if ( !Networking.IsHost ) return;

		var damageScale = MathX.Remap( distance, MinimumFallDistance, DeathFallDistance, 0, 1 );
		int damageAmount = (int)(damageScale * 100 * DamageMultiplier);
		if ( damageAmount < 1 ) return;

		// play smashed legs on the ground sound

		if ( Player is IDamageable damage )
		{
			Log.Info( $"PlayerFallDamage: dealing {damageAmount} to {Player.DisplayName} (dist: {distance}, vel: {velocity})" );

			var dmg = new DamageInfo( damageAmount, GameObject, null );
			dmg.Tags.Add( DamageTags.Fall );

			damage.OnDamage( dmg );

			PlayFallSound();
		}
	}
}