LavaPuddle.cs

Component that represents a lava puddle effect attached to a game object. It updates a decal size and tint over its lifetime, plays ambient SFX on spawn, and periodically damages the local player if inside the puddle.

NetworkingFile Access
using System;
using Sandbox;
using static Sandbox.VertexLayout;

/// <summary>
/// LavaPuddle is created on client and can't hurt proxy players.
/// </summary>
public class LavaPuddle : Component
{
	[Property] public Decal Decal { get; set; }

	private Dictionary<Player, bool> _playerShockwaveChecks;
	private List<Player> _shockwaveDamagedPlayers;

	public Enemy EnemySource { get; set; }
	public EnemyType EnemyType { get; set; }

	[Property, Hide] public float Damage { get; set; }
	[Property, Hide] public float Radius { get; set; }
	[Property, Hide] public float Lifetime { get; set; }
	[Property, Hide] public Color ColorA { get; set; }
	[Property, Hide] public Color ColorB { get; set; }
	[Property, Hide] public float AlphaMax { get; set; }

	public TimeSince TimeSinceSpawn { get; set; }
	private TimeSince _timeSinceDamagePlayer;

	private const float DAMAGE_INTERVAL = 0.33f;

	protected override void OnStart()
	{
		base.OnStart();

		if ( IsProxy )
			return;

		_playerShockwaveChecks = new();
		_shockwaveDamagedPlayers = new();

		TimeSinceSpawn = 0f;

		Manager.Instance.PlaySfxNearby( "lava_puddle_02", (Vector2)WorldPosition, pitch: Game.Random.Float( 0.9f, 1.1f ), volume: 1.4f, maxDist: 500f );

		if ( Scene.GetAll<LavaPuddle>().Count() <= 1 )
			Manager.Instance.PlaySfxNearby( "lava_puddle_01", (Vector2)WorldPosition, pitch: Game.Random.Float( 0.9f, 1.1f ), volume: 1.4f, maxDist: 500f );
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		if ( Manager.Instance.IsGameOver )
			return;

		var pos = (Vector2)WorldPosition;

		if ( TimeSinceSpawn > Lifetime )
		{
			GameObject.Destroy();
			return;
		}
		else
		{
			var decalSize = Radius * 0.285f * Utils.Map( TimeSinceSpawn, 0f, 0.5f, 0f, 1f, EasingType.SineOut );
			Decal.Size = new Vector2( decalSize, decalSize );

			var color = Color.Lerp( ColorA, ColorB, 0.5f + Utils.FastSin(Time.Now * 12f) * 0.5f );
			Decal.ColorTint = color.WithAlpha( Utils.Map( TimeSinceSpawn, Lifetime - 0.5f, Lifetime, AlphaMax, 0f ) );

			if ( TimeSinceSpawn > 0.5f && TimeSinceSpawn < Lifetime - 0.5f && _timeSinceDamagePlayer > DAMAGE_INTERVAL )
			{
				var player = Manager.Instance.LocalPlayer;
				if ( !player.IsValid() )
					return;

				var lavaPos = (Vector2)WorldPosition;

				float distSqr = (player.Position2D - lavaPos).LengthSquared;
				if ( distSqr < MathF.Pow( Radius, 2f ) && !player.IsDead && player.TimeSinceHurtLava > DAMAGE_INTERVAL)
				{
					var dir = Utils.GetRandomVector();
					player.DamageRpc( Damage, DamageType.LavaPuddle, player.Position2D, dir, upwardAmount: Game.Random.Float(0f, 0.2f), force: 0f, ragdollForce: 0f, EnemySource, EnemyType );

					player.TimeSinceHurtLava = 0f;
					_timeSinceDamagePlayer = 0f;
				}
			}
		}

		//Gizmo.Draw.Color = Color.White.WithAlpha( 0.8f + Utils.FastSin( Time.Now * 12f ) * 0.2f );// * Utils.Map( _shockwaveTimer, Lifetime - 0.25f, Lifetime, 1f, 0f );
		//Gizmo.Draw.LineThickness = 1f;
		//Gizmo.Draw.LineCircle(
		//	center: (Vector3)pos + Vector3.Up * 1f,
		//	forward: Vector3.Up,
		//	radius: Radius,
		//	startAngle: Time.Now * -150f,
		//	totalDegrees: 360f,
		//	sections: 50
		//);

		if ( IsProxy )
			return;

	}
}