PackOpeningPresentation.cs

Presentation timing helper for a card pack opening. Takes a committed PackOpening receipt, picks the star card, orders the remaining rewards and computes when each card reveals based on tier and animation age.

using System;
using System.Linq;

namespace Socer;

// Presentation only. The receipt has already been committed by ClubCareer.OpenOwnedPack.
public sealed class PackOpeningPresentation
{
    public PackOpening Receipt { get; }
    public PackReward[] Rewards { get; }
    public PackReward Star { get; }
    public int Tier { get; }
    public float RevealAt { get; }
    public float CompleteAt => RevealAt + .3f;
    public float SpreadDuration => Math.Max(.7f,.18f+Math.Max(0,Rewards.Length-2)*.14f+.62f);
    public float SignalAt => Math.Max(1.55f, RevealAt - 1.65f);
    public float HoldAt => RevealAt - .38f;
    public string Material => Tier switch {4=>"legendary",3=>"elite",2=>"gold",1=>"silver",_=>"bronze"};

    public PackOpeningPresentation(PackOpening receipt)
    {
        Receipt=receipt;
        var items=receipt?.Items?.Where(i=>i!=null).ToArray()??Array.Empty<PackReward>();
        Star=items.Where(i=>i.Player!=null).OrderByDescending(i=>CardRatings.RevealRank(i.Player))
            .ThenByDescending(i=>i.Player.Overall).ThenBy(i=>i.Player.CatalogId,StringComparer.Ordinal).FirstOrDefault()
            ??items.OrderByDescending(i=>i.RevealValue).FirstOrDefault();
        Rewards=(Star==null?Array.Empty<PackReward>():new[]{Star}).Concat(items.Where(i=>!ReferenceEquals(i,Star))
            .OrderByDescending(i=>i.Player!=null).ThenByDescending(i=>i.RevealValue).ThenBy(i=>i.Name,StringComparer.Ordinal)).ToArray();
        Tier=Star?.Player==null?Math.Clamp((Star?.RevealValue??0)/1000-1,0,2):CardRatings.RevealRank(Star.Player);
        RevealAt=Tier switch {4=>6.9f,3=>5.25f,2=>3.45f,_=>2.15f};
    }
    public float OtherRevealAt(int index)=>.18f+Math.Max(0,index-1)*.14f;
    public bool Visible(int index,float age,float spreadAge=-1)
    {
        if(index<0 || index>=Rewards.Length)return false;
        if(ReferenceEquals(Rewards[index],Star))return age>=RevealAt;
        return age>=RevealAt && spreadAge>=OtherRevealAt(index);
    }
    public int Revealed(float age,float spreadAge=-1)=>Enumerable.Range(0,Rewards.Length).Count(i=>Visible(i,age,spreadAge));
    public float Next(float age)
    {
        foreach(float milestone in new[]{.95f,1.55f,SignalAt,HoldAt,RevealAt+.01f,CompleteAt})
            if(milestone>age+.08f)return milestone;
        return CompleteAt;
    }
}