PopRiotHud.razor
@using Sandbox
@using Sandbox.UI
@using PaintballBaddies
@inherits PanelComponent
@namespace PaintballBaddies
<root>
@if(Visible)
{
    <div class="riot-guide @(Session.RiotLives<=2 || (Session.RiotNoticeSeconds>0 && Session.RiotNotice.Contains(" HIT")) ? "critical" : "")">
        <div class="heading"><label>@($"WAVE {System.Math.Max(1,Session.RiotWave)}")</label><label class="score">@($"{Session.TeamScore} PTS · x{Session.RiotMultiplier}")</label></div>
        <div class="lives">
            @for(int i=0;i<MultiplayerSession.RiotMaximumLives;i++){<label class="life @(i<Session.RiotLives ? "full" : "lost")">shield</label>}
            <label class="life-count">TEAM LIVES</label>
        </div>
        @if(Session.RiotBreather>0){<label class="hint">@Prompt</label>}
        else if(Session.RiotNoticeSeconds>0 && Controls?.ShowGameplayText!=false){<label class="hint">@Session.RiotNotice</label>}
    </div>
    @if(!string.IsNullOrEmpty(ThreatHint)){<div class="riot-threat"><label>@ThreatHint</label><label class="dodge">SHOOT IT · DODGE · TAKE COVER</label></div>}
}
</root>
@code {
    MultiplayerSession Session=>MultiplayerSession.Find(Scene);
    PaintballControls Controls=>Scene.GetAllComponents<PaintballControls>().FirstOrDefault();
    bool Visible=>Session.IsValid() && Session.IsPopRiot && Session.InRound && Controls?.Open!=true;
    string Prompt=>Session.RiotBreather>0 ? $"DRONES INCOMING FROM ABOVE · {(int)System.MathF.Ceiling(Session.RiotBreather)}" : "SHOOT DRONES · INTERCEPT THEIR PAINTBALLS";
    string ThreatHint
    {
        get
        {
            var player=MultiplayerSession.LocalPlayer(Scene);if(!player.IsValid())return "";
            var actor=player.Components.Get<PaintballCombatant>();
            if(Scene.GetAllComponents<PopRiotTarget>().Any(t=>t.Crashing && (t.WorldPosition-player.EyePosition).Length<240))
                return "FALLING DRONE · SHOOT AGAIN OR MOVE";
            var target=Session.RiotTargets.FirstOrDefault(t=>t.Charging && t.AttackTarget==actor);
            bool incoming=false;
            if(!target.IsValid())
            {
                target=Session.RiotTargets.Where(t=>t.Kind==PopKind.Glob && t.GlobVelocity.Length>1
                    && (t.WorldPosition-player.EyePosition).Length<700
                    && Vector3.Dot((player.EyePosition-t.WorldPosition).Normal,t.GlobVelocity.Normal)>.94f)
                    .OrderBy(t=>(t.WorldPosition-player.EyePosition).Length).FirstOrDefault();
                incoming=target.IsValid();
            }
            if(!target.IsValid())return "";
            var local=player.EyeAngles.ToRotation().Inverse*(target.WorldPosition-player.EyePosition).Normal;
            string direction=local.x<-.5f ? "BEHIND" : local.y>.4f ? "LEFT" : local.y<-.4f ? "RIGHT" : "AHEAD";
            return $"{(incoming ? "INCOMING PAINTBALL" : "DRONE LOCK")} · {direction}";
        }
    }
    protected override int BuildHash()=>!Visible ? 0 : System.HashCode.Combine(Session.TeamScore,Session.RiotMultiplier,
        Session.RiotLives,Session.RiotWaveCleared,Prompt,ThreatHint,Session.RiotNoticeSeconds>0 ? Session.RiotNotice : "",System.HashCode.Combine(Session.RiotWave,Controls?.ShowGameplayText));
}