using UnityEngine;
namespace ProjectM.Client
{
///
/// Pure HUD presentation math (unit-tested, no ECS / no UnityEngine.Object). Drives the low-health screen
/// vignette + the transient hurt-flash so the damage feedback is deterministic and testable. Used by the
/// client-only observe-only in PresentationSystemGroup; the flash decay runs on the
/// wall-frame SystemAPI.Time.DeltaTime, which is correct in a presentation system (the dt-trap only
/// applies to plain simulation systems).
///
public static class HudVisualMath
{
/// Below this health fraction the low-health vignette starts to ramp in.
public const float LowHealthThreshold = 0.35f;
/// Maximum steady vignette opacity at 0 HP.
public const float MaxVignetteOpacity = 0.55f;
/// Opacity boost added by a fresh hit (a hurt flash), decaying back to the steady vignette.
public const float HurtFlashKick = 0.40f;
/// Seconds for a full hurt-flash kick to decay to zero.
public const float HurtFlashDuration = 0.40f;
/// 0 at/above the low-health threshold, ramping to 1 as the health fraction falls to 0.
public static float VignetteIntensity(float healthFrac)
{
float f = Mathf.Clamp01(healthFrac);
if (f >= LowHealthThreshold) return 0f;
return Mathf.Clamp01((LowHealthThreshold - f) / LowHealthThreshold);
}
/// Steady low-health vignette opacity (no flash) from the current health fraction.
public static float VignetteOpacity(float healthFrac) => VignetteIntensity(healthFrac) * MaxVignetteOpacity;
/// Decay an active hurt-flash boost toward 0 over seconds (clamped, never negative).
public static float DecayFlash(float flash, float dt)
=> Mathf.Max(0f, flash - (HurtFlashKick / HurtFlashDuration) * Mathf.Max(0f, dt));
/// Final overlay opacity: the steady low-health vignette plus the current flash boost, clamped to 1.
public static float CombinedOpacity(float healthFrac, float flash)
=> Mathf.Clamp01(VignetteOpacity(healthFrac) + Mathf.Clamp01(flash));
}
}