MatchHud.razor

The in-match HUD panel. Renders the broadcast view, coach sidebar, team tactics and coach command bar, playback speed and camera toggles, substitution and injury panels, match result, and the intro ceremony overlay. Rebuilds around ten times a second via BuildHash and stops rebuilding while the club screen is open.

Rough Code
@using Sandbox
@using Sandbox.UI
@using Socer
@using System
@using System.Linq
@using System.Globalization
@inherits PanelComponent

<root class="broadcast-focus @((Game?.ClubOpen==true || Game?.StrategyLabOpen==true) ? "hidden" : Game?.MatchIntroActive==true?"ceremony-active":"") @(Game?.FootballLabActive==true?"football-lab-active":"") @(Game?.MatchTutorialActive==true?"match-tutorial-active":"")">
@if (Game is not null)
{
    <div class="main">
        <div class="field-area">
            <div class="field-heading"><label>@(Game.FootballLabActive?"FOOTBALL LAB":Game.Career?.Pending!=null?Game.Career.Pending.Cup?"CUP · 11 × 11":"LEAGUE · 11 × 11":"MATCH · 11 × 11")</label><label>@($"{M.AwayName}: {UiEnglish.Text(MatchSimulation.ApproachNames[M.Approach[1]])}")</label><label>@(M.Direction(0)>0 ? $"{M.HomeName} →     ← {M.AwayName}" : $"{M.AwayName} →     ← {M.HomeName}")</label></div>
            <BroadcastView Game="@Game" />
            <div class="ticker"><div class="live-dot"></div><label>@UiEnglish.Text(M.Status)</label><label class="ball-state">@(M.Owner>=0 ? $"Ball: #{M.Players[M.Owner].Number}" : "Ball in play")</label></div>
            <div class="director-strip"><label class="@(Game.Director.PlaybackRate<.6f ? "slow-label" : "")">@UiEnglish.Text(Game.Director.Caption)</label><label>@(Game.Director.PlaybackRate.ToString("0.0",CultureInfo.InvariantCulture)+"×")</label><label class="director-note">@(Game.Director.Automatic ? "AUTO MODE" : "MANUAL MODE")</label></div>
        </div>
        <div class="sidebar"><CoachSidebar Game="@Game" /></div>
    </div>
    @if(Game.FootballLabActive && !Game.MatchTutorialActive){<FootballLabPanel Game="@Game" />}
    @if(!Game.MatchTutorialActive)
    {
    <div class="coach-bar">
        <div class="team-tactic-row">
            <button class="club-open" onclick=@(()=>Game.OpenClub())>My club</button>
            <label>TEAM</label>
            <button class="team-counter @(M.Tactics[0]==0?"selected":"")" onclick=@(()=>SetTactic(0))>Counterattack</button>
            <button class="team-balance @(M.Tactics[0]==1?"selected":"")" onclick=@(()=>SetTactic(1))>Balanced</button>
            <button class="team-high-press @(M.Tactics[0]==2?"selected":"")" onclick=@(()=>SetTactic(2))>High press</button>
            <label>@UiEnglish.Text(M.CoachCaption)</label>
            <button class="coach-press" onclick=@(()=>M.Command(1))>Press</button>
            <button class="coach-attack" onclick=@(()=>M.Command(2))>Attack</button>
            <button class="coach-rest" onclick=@(()=>M.Command(3))>Conserve energy</button>
            @if(M.Seconds<M.CoachReadyAt){<label>@($"Coach command ready in {Math.Ceiling(M.CoachReadyAt-M.Seconds)} s")</label>}
        </div>
        @if(Game.SelectedMatchStrategy!=null)
        {
            <div class="playbook-row">
                <label>PLAYS</label>
                <button class="auto-strategies @(Game.AutoStrategies?"selected":"")" onclick=@(()=>Game.ToggleAutomaticStrategies())>@(Game.AutoStrategies?"Auto plays: ON":"Auto plays: OFF")</button><button onclick=@(()=>Game.MatchStrategyIndex=(Game.MatchStrategyIndex+1)%Game.StrategyLibrary.Count)>@UiEnglish.Text(Game.SelectedMatchStrategy.Name,Game.SelectedMatchStrategy.Name) · @Game.SelectedMatchStrategy.EstimatedSuccessPercent()% ↻</button><button onclick=@(()=>Game.CallSavedStrategy())>Run play</button>
                @if(M.Strategy!=null){<label>@UiEnglish.Text(M.Strategy.Plan.Name+" · "+M.Strategy.Status,"Play in progress")</label>}
            </div>
        }
    </div>
    <div class="controls">
        <button class="pause primary" disabled="@(Game.SubstitutionOpen || Game.InjuryChoiceOpen)" onclick=@(()=>Game.Paused=!Game.Paused)>@(Game.Paused ? "Resume" : "Pause")</button>
        @if(Game.CanFinishCurrentMatchQuickly && !Game.MediaLeagueActive){<button class="finish-in-text" onclick=@(()=>Game.FinishCurrentMatchQuickly())>@(GamePreferences.Russian?"Завершить текстом →":"Finish in text →")</button>}
        @if(!Game.MediaLeagueActive)
        {
        <button class="open-substitutions" disabled="@(M.MedicalActive || M.Finished || M.ShootoutActive || Game.IsRulesPreview || Game.MatchIntroActive || Game.Replay.Playing)" onclick=@(()=>Game.OpenSubstitutions())>@($"Substitutions {Game.SubstitutionsUsed}/{MatchController.SubstitutionLimit}")</button>
        <button class="auto-speed @(Game.Director.Automatic ? "selected" : "")" onclick=@(()=>Game.EnableAutomatic())>Auto</button>
        <button class="speed-one @(!Game.Director.Automatic && Game.Speed==1 ? "selected" : "")" onclick=@(()=>Game.Speed=1)>1×</button>
        <button class="speed-half @(!Game.Director.Automatic && Game.Speed==.5f ? "selected" : "")" onclick=@(()=>Game.Speed=.5f)>0.5×</button>
        <button class="speed-fast @(!Game.Director.Automatic && Game.Speed==1.5f ? "selected" : "")" onclick=@(()=>Game.Speed=1.5f)>1.5×</button>
        }
        <button class="camera-toggle @(Game.Broadcast.Enabled && !Game.ManualCameraActive ? "selected" : "")" onclick=@(()=>Game.ToggleBroadcastCamera())>@(Game.ManualCameraActive?"Resume camera":Game.Broadcast.Enabled?"Camera":"Full pitch")</button>
        <button class="vision-toggle @(Game.ShowVision ? "selected" : "")" onclick=@(()=>Game.ShowVision=!Game.ShowVision)>Vision</button>
        <button class="speech-toggle @(Game.ShowSpeech ? "selected" : "")" onclick=@(()=>Game.ShowSpeech=!Game.ShowSpeech)>Speech</button>
        @if(!M.ShootoutActive && !Game.MediaLeagueActive){<button class="manual-standards @(Game.ManualSetPieces?"selected":"")" onclick=@(()=>Game.ManualSetPieces=!Game.ManualSetPieces)>@(Game.ManualSetPieces?"Set pieces: manual":"Set pieces: auto")</button>}
        @if(!Game.MediaLeagueActive)
        {
            <button class="club-open" onclick=@(()=>Game.OpenClub())>My club</button>
            <label class="hint">@($"{GameHotkeys.Display("match.pause")} · {(GamePreferences.Russian?"Пауза":"Pause")} / {GameHotkeys.Display("match.slower")} {GameHotkeys.Display("match.faster")} · {(GamePreferences.Russian?"Скорость":"Speed")} / {GameHotkeys.Display("match.auto_speed")} · Auto")</label>
        }
        @if(Game.Career?.Pending==null && !Game.MediaLeagueActive)
        {
            @if(M.Finished){<button class="exit-match primary" onclick=@(()=>Game.ExitFinishedMatch())>Exit to career</button>}
            @if(!Game.MustFinishMatchBeforeLeaving && !Game.IsRulesPreview)
            {
                <button class="replay" onclick=@(()=>Game.Restart(false))>Rematch</button>
                <button class="new-match" onclick=@(()=>Game.Restart(true))>New match</button>
            }
        }
    </div>
    }
    @if(Game.SubstitutionOpen && (!Game.MatchTutorialActive || Game.MatchTutorialSubstitutionLesson) && !Game.MediaLeagueActive){<SubstitutionPanel Game="@Game" />}
    @if(Game.InjuryChoiceOpen){<InjurySubstitutionPanel Game="@Game" />}
    @if(M.Finished && !Game.IsRulesPreview && !Game.MatchTutorialActive && !Game.MediaLeagueActive)
    {
        <MatchResult Game="@Game" />
    }
    @if(Game.MatchIntroActive && !Game.MatchTutorialActive)
    {
        <div class="match-intro ceremony-intro"><div class="ceremony-caption"><label class="intro-kicker">SOCER · MATCHDAY</label><div class="intro-clubs"><img src="@ClubIdentity.Crest(M.HomeName)" /><label class="intro-title">@($"{M.HomeName} / {M.AwayName}")</label><img src="@ClubIdentity.Crest(M.AwayName)" /></div><label class="intro-caption">@(Game.MatchIntroAge<2.8f?"Teams greet the crowd":Game.MatchIntroAge<5?"Ready for kick-off":"Kick-off")</label></div><button class="skip-intro" onclick=@(()=>Game.SkipMatchIntro())>Skip →</button></div>
        <div class="ceremony-fade" style="@FormattableString.Invariant($"opacity:{Math.Clamp(1-Math.Abs(Game.MatchIntroAge-6.1f)/.3f,0,1):0.000};")"></div>
    }
}

</root>

@code {
    protected override void OnTreeBuilt(){base.OnTreeBuilt();UiRussian.Apply(Panel);}
    MatchController cachedGame;
    MatchController Game
    {
        get
        {
            if(cachedGame?.GameObject?.IsValid!=true || cachedGame.Scene!=Scene)
                cachedGame=Scene.GetAllComponents<MatchController>().FirstOrDefault();
            return cachedGame;
        }
    }
    MatchSimulation M => Game.Match;
    void SetTactic(int value) { Game.SetClubTactic(value); }
    protected override int BuildHash() => Game?.ClubOpen==true?0:HashCode.Combine(Game?.SelectedPlayer,(int)(Time.Now*10));
}