UI/BloodCounter.razor
@using Sandbox;
@using Sandbox.UI;
@using SquishyTerry.Scoring;
@namespace SquishyTerry
@inherits PanelComponent

@*
	The blood score: top centre while a knife run is bleeding. The number ticks up with the bleeding
	and punches on every gush (a stab, a squish). When the run ends it holds the final amount for a
	moment ("NEW BEST!" if it beat the player's record), then fades.
*@

<root>
	@if ( _alpha > 0.001f )
	{
		<div class="blood" style="opacity: @(_alpha.ToString( "0.##" )); transform: scale( @(_punch.ToString( "0.###" )) );">
			<div class="title">@(_final ? "BLOOD LOST" : "BLEEDING")</div>
			<div class="amount">
				<label class="number">@_shown.ToString( "N0" )</label>
				<label class="unit">ml</label>
			</div>
			@if ( _final && _best )
			{
				<div class="best">NEW BEST!</div>
			}
		</div>
	}
</root>

@code
{
	const float HoldTime = 3f;
	const float FadeTime = 0.5f;

	ScoreKeeper _keeper;
	int _shown;
	float _punch = 1f;
	float _alpha;
	bool _final, _best;
	float _finalT;

	protected override void OnUpdate()
	{
		if ( _keeper is null )
		{
			_keeper = Scene.GetAllComponents<ScoreKeeper>().FirstOrDefault();
			if ( _keeper is null ) return;
			_keeper.BloodScored += OnScored;
		}

		if ( _keeper.Bleeding )
		{
			int ml = _keeper.BloodMl;
			if ( ml - _shown >= 30 ) _punch = 1.25f; // a gush
			_shown = ml;
			_final = false;
			_alpha = System.MathF.Min( _alpha + RealTime.Delta * 6f, 1f );
		}
		else if ( _final )
		{
			_finalT += RealTime.Delta;
			_alpha = 1f - System.Math.Clamp( (_finalT - HoldTime) / FadeTime, 0f, 1f );
		}
		else _alpha = System.MathF.Max( _alpha - RealTime.Delta * 4f, 0f );

		_punch += (1f - _punch) * System.MathF.Min( RealTime.Delta * 10f, 1f );
	}

	void OnScored( int ml, bool best )
	{
		_shown = ml;
		_final = true;
		_best = best;
		_finalT = 0f;
		_punch = 1.3f;
	}

	protected override void OnDisabled()
	{
		if ( _keeper is not null ) _keeper.BloodScored -= OnScored;
		_keeper = null;
	}

	// Rebuild only when something visible changes.
	protected override int BuildHash() => System.HashCode.Combine( _shown, (int)(_alpha * 50), (int)(_punch * 100), _final, _best );
}