RoundHighlights.cs
using Sandbox;
using System;
namespace PaintballBaddies;

/// <summary>Local personal records and awards derived from existing match statistics.</summary>
public sealed class RoundHighlights
{
    public const string CookieKey="paintball-personal-bests-v1";
    private readonly string cookieKey;
    private string StorageKey=>cookieKey ?? CookieKey;
    public RoundHighlights(string cookieKey=CookieKey){this.cookieKey=cookieKey;}
    public sealed record Award(string Title,string Winners,string Detail);
    private Dictionary<string,int> records;
    private string recordedRound;
    private int? previousBest;
    public bool NewBest {get;private set;}
    public int? Best {get;private set;}
    public int? ResultPoints {get;private set;}
    public bool ObservedRound {get;private set;}
    public Dictionary<string,int> Snapshot()=>new(records ??= Game.Cookies.Get(StorageKey,new Dictionary<string,int>()));

    public static string RecordKey(string map,bool online,int minutes)=>$"{map}|{(online ? "multiplayer" : "solo")}|{minutes}";
    public int? Read(string key)
    {
        records??=Game.Cookies.Get(StorageKey,new Dictionary<string,int>());
        return records.TryGetValue(key,out var value) ? value : null;
    }
    public void WatchActiveRound()
    {
        ObservedRound=true;recordedRound=null;ResultPoints=null;NewBest=false;Best=null;
    }
    public void ClearObservation(){ObservedRound=false;recordedRound=null;ResultPoints=null;NewBest=false;Best=null;}
    public void Complete(string key,int serial,int score)
    {
        if(!ObservedRound)return;
        var token=$"{key}|{serial}";
        if(recordedRound!=token){previousBest=Read(key);recordedRound=token;ResultPoints=null;}
        if(ResultPoints==score)return;
        ResultPoints=score;
        NewBest=previousBest is null || score>previousBest;
        Best=previousBest is null ? score : Math.Max(previousBest.Value,score);
        // Replicated final scores can arrive after TIME UP. Reconcile against the
        // previous round's record, so a transient score is not kept as a false best.
        if(Read(key)!=Best){records[key]=Best.Value;Game.Cookies.Set(StorageKey,records);}
    }
    public static Award[] Awards(ArenaMatch.Standing[] standings)
    {
        var rows=standings ?? Array.Empty<ArenaMatch.Standing>();
        string Names(ArenaMatch.Standing[] winners)
        {
            var names=winners.Take(2).Select(x=>x.IsPlayer ? $"{x.Name} (YOU)" : x.Name);
            return string.Join(" / ",names)+(winners.Length>2 ? $" +{winners.Length-2} tied" : "");
        }
        Award Most(string title,Func<ArenaMatch.Standing,int> count,string unit)
        {
            int max=rows.Select(count).DefaultIfEmpty().Max();
            var winners=rows.Where(x=>count(x)==max).ToArray();
            return max>0 ? new(title,Names(winners),$"{max} {unit}{(winners.Length>1 ? " / TIED" : "")}") : new(title,"No winner yet",$"No {unit.ToLowerInvariant()}");
        }
        var active=rows.Where(x=>x.Landed+x.Received+x.Knockdowns+x.KnockedDown>0).ToArray();
        if(rows.Any(x=>x.Survival))return new[]{Most("TOP CHEF",x=>x.SausageKills,"SAUSAGES DEFEATED"),Most("HEAD CHEF",x=>x.HeadshotKills,"HEADSHOT KILLS"),Most("TEAM MVP",x=>x.Points,"POINTS CONTRIBUTED")};
        if(rows.Any(x=>x.PopRiot))return new[]{Most("TEAM MVP",x=>x.Banked,"POINTS CONTRIBUTED"),Most("BUBBLE BUSTER",x=>x.Carried,"TARGETS POPPED"),Most("SHARP SHOT",x=>x.Landed,"TARGET HITS")};
        int least=active.Select(x=>x.Received).DefaultIfEmpty().Min();
        var clean=active.Where(x=>x.Received==least).ToArray();
        return new[]{Most("TOP TAGGER",x=>x.Landed,"TAGS"),
            active.Length>0 ? new Award("CLEANEST SUIT",Names(clean),$"{least} HITS TAKEN{(clean.Length>1 ? " / TIED" : "")}") : new Award("CLEANEST SUIT","No winner yet","No active competitors"),
            Most("MOST KNOCKDOWNS",x=>x.Knockdowns,"KNOCKDOWNS")};
    }
}