UI/PlayerHud/Components/Health.razor

Razor UI component for the player HUD health display. It renders current health (with overheal icon), temporary heal/damage floating numbers, and updates visibility/timers each Tick. It exposes Instance and handlers OnHealing and OnDamage to update screen number elements.

@namespace KOTH.UI
@inherits Panel
@attribute [StyleSheet]

@if ( !Player.IsValid() )
    return;

<root>
    @if (Player.DamageComponent.IsOverhealed())
    {
        <img class="hp-img" src="/ui/koth/hp.png"/>
    }
    <label class="hp" style="color: white;">@(Player.DamageComponent.GetHealthString() ?? "∞")</label>
    @if (CurrentHealthElement.IsVisable)
    {
        <label class="hp-noti">@(CurrentHealthElement)</label>
    }
    @* we aint got else ifs? *@
    @if (!CurrentHealthElement.IsVisable && CurrentDamageElement.IsVisable)
    {
        <label class="dmg-noti">@(CurrentDamageElement)</label>
    }
</root>

@code
{
    public static Health Instance { get; set; }

    public Health()
    {
        Instance = this;
    }

    public PlayerPawn Player => PlayerState.Local.PlayerPawn;

    FScreenNumberElement CurrentHealthElement = new()
        {
            IsDamage = false,
            IsVisable = false,
        };

    FScreenNumberElement CurrentDamageElement = new()
        {
            IsDamage = true,
            IsVisable = false,
        };

    public override void Tick()
    {
        base.Tick();

        if (CurrentHealthElement.DisplayedTime < -1)
        {
            CurrentHealthElement.IsVisable = false;
        }

        if (CurrentDamageElement.DisplayedTime < -1)
        {
            CurrentDamageElement.IsVisable = false;
        }
    }

    protected override int BuildHash()
    {
        if (!Player.IsValid())
            return 0;

        return HashCode.Combine(Time.Now);
    }

    public void OnHealing(FHealingReceived HealingDone)
    {
        if (HealingDone.Heals < 0.05f || HealingDone.HealingType == EHealingType.Continuous)
        {
            return;
        }

        if (!CurrentHealthElement.IsVisable)
        {
            CurrentHealthElement.Damage = 0;
            CurrentHealthElement.IsVisable = true;
        }
        
        CurrentHealthElement.DisplayedTime = 0;
        CurrentHealthElement.AddDamage(HealingDone.Heals);
    }

    public void OnDamage(FDamageTaken DamageTaken)
    {
        if (DamageTaken.Damage < 0.05f)
        {
            return;
        }

        if (!CurrentDamageElement.IsVisable)
        {
            CurrentDamageElement.Damage = 0;
            CurrentDamageElement.IsVisable = true;
        }

        CurrentDamageElement.DisplayedTime = 0;
        CurrentDamageElement.AddDamage(DamageTaken.Damage);
    }
}