A UI Razor component that displays active timed powerups as icons with countdown seconds. It enumerates NZombies.ActivePowerups.All(), shows each powerup icon and remaining time, and uses a hashed BuildHash based on whole-second remaining times and HUD theme to optimize redraws.
@using Sandbox;
@using Sandbox.UI;
@using System.Linq;
@using NZombies;
@inherits PanelComponent
@*
POWERUP TIMERS — icon plus countdown for whatever is running.
⚠️ Only TIMED powerups appear. Max Ammo, Bonus Points and Nuke are instant
(`duration = 0` in the original) and announce themselves with the banner instead;
membership of `ActivePowerups.Durations` is what decides, so there is no second
list here to fall out of step with it.
⛔ The original gives each powerup a FIXED slot index (`powerup_poses`, seven
positions offset from centre, with a second row above). That matters once several
can run at once — a slot that slides sideways when a neighbour expires is worse
than a gap. With two powerups a centred row reads fine; the fixed-slot layout is
the right build when there are more.
*@
<root class="pw-timers @HudTheme.Class">
@if ( Any )
{
<div class="row">
@foreach ( var (kind, remaining) in NZombies.ActivePowerups.All() )
{
<div class="slot @(remaining <= 5f ? "ending" : "")">
<img class="icon" [email protected]( kind ) />
<div class="time">@remaining.CeilToInt()</div>
</div>
}
</div>
}
</root>
@code
{
bool Any => NZombies.ActivePowerups.All().Any();
/// <summary>
/// ⚠️ Hashes the WHOLE ROW as a string, bucketed to whole seconds. Hashing the
/// raw remaining floats rebuilds every frame for thirty seconds per powerup; the
/// display only ever shows integers, so a second is the smallest change that can
/// possibly matter.
/// </summary>
protected override int BuildHash()
{
var key = string.Join( ",", NZombies.ActivePowerups.All()
.Select( p => $"{p.kind}:{p.remaining.CeilToInt()}" ) );
return System.HashCode.Combine( key, HudTheme.Class );
}
}