UI/PowerupBannerHud.razor

A Razor UI component for the powerup banner shown center-screen when a player picks up a powerup. It renders the banner text while showing and computes an opacity that holds at full for 60% of the duration then fades, and throttles rebuilds by bucketing the fade into 20 steps in BuildHash.

File Access
@using Sandbox;
@using Sandbox.UI;
@using NZombies;
@inherits PanelComponent

@*
    POWERUP BANNER — the name of what you just picked up, centre screen.

    ⚠️ ITS OWN PanelComponent rather than a block inside SurvivalHud. The HUD hides
    itself in creative and behind the lobby (see its `Visible`), and a powerup
    collected in creative should still tell you it worked — that is most of how this
    gets tested. Keeping it separate also means its fade cannot be throttled by the
    HUD's deliberately coarse BuildHash.

    ⛔ The original shows TIMED powerups as a row of icons with countdowns
    (cl_hud.lua's `powerup_poses`, seven slots offset from centre). Max Ammo is
    INSTANT and never appears there — what it shows is the name. That row is the
    right thing to build when the first timed powerup exists; this is not it.
*@

<root class="@HudTheme.Class">
    @if ( PowerupBannerState.Showing )
    {
        <div class="banner" style="opacity: @Fade">@PowerupBannerState.Text</div>
    }
</root>

@code
{
    /// <summary>
    /// Fades over the last part of its life.
    ///
    /// ⚠️ HOLDS AT FULL, THEN FADES — it does not fade the whole time. A banner that
    /// starts dimming the instant it appears is never seen at full strength, and this
    /// one has to be readable in a firefight.
    /// </summary>
    float Fade
    {
        get
        {
            var d = PowerupBannerState.Duration;
            var t = PowerupBannerState.Elapsed;

            // Full for the first 60%, then out.
            var holdUntil = d * 0.6f;
            if ( t <= holdUntil ) return 1f;

            var remaining = MathX.Clamp( (d - t) / System.MathF.Max( d - holdUntil, 0.01f ), 0f, 1f );
            return remaining;
        }
    }

    /// <summary>
    /// ⚠️ REBUILDS ON THE FADE, bucketed to 20 steps. The opacity is inline, so the
    /// tree has to rebuild for it to move — but hashing the raw float rebuilds every
    /// frame for 2.5s. Twenty steps is smooth at this size and costs a twentieth of
    /// the rebuilds.
    /// </summary>
    protected override int BuildHash() => System.HashCode.Combine(
        PowerupBannerState.Showing, PowerupBannerState.Text, (int)(Fade * 20f), HudTheme.Class );
}