RelegationMatch.cs

Part of the match controller. Handles the relegation choice overlay, media league match setup and preview mode, fine acceptance, and finishing or restarting the media presentation.

using Sandbox;
using Socer;
using System.Linq;

public sealed partial class MatchController
{
    public bool RelegationChoiceOpen {get;private set;}
    public bool MediaLeagueActive {get;private set;}
    public bool MediaLeaguePreview {get;private set;}
    public bool CanLeaveMediaLeague=>MediaLeagueActive && Match.IsMediaLeague && Match.Finished && !Match.Abandoned;
    public RelegationFinePlan RelegationFinePreview=>Career?.PreviewRelegationFine();
    bool mediaExitAllowed,mediaChoiceWasPaused;
    RelegationChoiceHud relegationHud;

    // Also adds the overlay to an already running editor session after hotload.
    void EnsureRelegationInterface()
    {
        if(relegationHud?.GameObject?.IsValid==true)return;
        relegationHud=Scene.GetAllComponents<RelegationChoiceHud>().FirstOrDefault();
        if(relegationHud!=null)return;
        var ui=Scene.GetAllComponents<MatchHud>().FirstOrDefault()?.GameObject;
        if(ui!=null)relegationHud=ui.Components.Create<RelegationChoiceHud>();
    }

    bool RequireRelegationResolved()
    {
        if(Career?.HasRelegationChoice!=true && !MediaLeagueActive)return true;
        OpenRelegationChoice();return false;
    }

    public void OpenRelegationChoice()
    {
        if(MediaLeaguePreview){ExitMediaLeaguePreview();return;}
        if(Career?.HasRelegationChoice!=true)return;
        if(GameSettingsOpen)ToggleGameSettings();
        if(!RelegationChoiceOpen)mediaChoiceWasPaused=Paused;
        CloseCommandWheel();CancelDirectOrder();SetFieldDragSlow(false);
        RelegationChoiceOpen=true;Paused=true;accumulator=0;Revision++;
    }
    public void CloseRelegationChoice()
    {
        if(!RelegationChoiceOpen)return;
        RelegationChoiceOpen=false;
        Paused=MediaLeagueActive?mediaChoiceWasPaused:true;
        accumulator=0;Revision++;
    }

    public void StartRelegationMediaMatch()
    {
        if(Career?.HasRelegationChoice!=true || Career.Pending!=null || QuickMatchRunning)return;
        if(MediaLeagueActive)
        {CloseRelegationChoice();ClubOpen=false;Paused=false;return;}
        int seed=Career.BeginRelegationMediaMatch();if(seed==0)return;
        StartMediaPresentation(seed,false);
        ClubNotice=RelegationText("Медиа-лига: досмотрите матч или выберите штраф.","Media league: watch the match to the end or choose the fine.");
        SaveCareer();Revision++;
    }

    // A career-preserving exhibition for viewing the same players and AI.
    public void StartMediaLeaguePreview(int seed)
    {
        if(MediaLeagueActive || QuickMatchRunning || Career?.HasRelegationChoice==true)return;
        StartMediaPresentation(seed,true);Revision++;
    }
    public void ExitMediaLeaguePreview()
    {
        if(!MediaLeaguePreview)return;
        FinishMediaPresentation();ClubOpen=true;Paused=true;Revision++;
    }

    void StartMediaPresentation(int seed,bool preview)
    {
        if(StrategyLabOpen)CloseStrategyLab();
        if(MatchTutorialActive)EndMatchTutorial();
        if(GameSettingsOpen)ToggleGameSettings();
        if(TutorialStep>=0)FinishTutorial(false);
        PreparingMatch=false;MatchModeSelection=false;QuickBroadcastOpen=false;
        // The preview mechanism preserves the club match, substitutions and camera.
        // The persistent obligation, rather than a competitive fixture, owns this match.
        ShowRulesPreview("football-ai");
        Match=MatchSimulation.MediaLeagueMatch(seed);
        MediaLeagueActive=true;MediaLeaguePreview=preview;RelegationChoiceOpen=false;
        FootballLabActive=false;PreviewManualSetPieces=false;PreviewCameraWidth=0;
        Director=new MatchDirector{Automatic=false,ManualSpeed=1.5f};Director.Update(Match,0);
        Broadcast=new BroadcastCamera();SelectedPlayer=OrderedPlayer=9;
        CloseCommandWheel();CancelDirectOrder();fieldPings.Clear();pingMatch=Match;
        manualSelectionMatch=Match;manualCameraMatch=Match;manualCameraUntil=0;FieldDragSlow=false;
        Replay.Reset();Replay.Capture(Match);observedGoals=0;replayAfterGoal=0;accumulator=0;
        Settlement=null;SkipMatchIntro();ClubOpen=false;Paused=false;
    }

    public void AcceptRelegationFine(string previewKey)
    {
        if(Career?.HasRelegationChoice!=true)return;
        if(!Career.ApplyRelegationFine(previewKey))
        {
            ClubNotice=RelegationText("Состав или баланс изменился. Проверьте новый расчёт штрафа.","Your squad or balance changed. Review the updated fine.");
            Revision++;return;
        }
        FinishMediaPresentation();RelegationChoiceOpen=false;
        ClubOpen=true;Paused=true;ClubPage="Главная";ClubNotice=Career.LastResult;
        SaveCareer();Revision++;
    }
    public void CompleteMediaLeagueMatch()
    {
        if(MediaLeaguePreview){ExitMediaLeaguePreview();return;}
        if(!CanLeaveMediaLeague || !Career.ResolveRelegationMediaMatch(Match))return;
        FinishMediaPresentation();RelegationChoiceOpen=false;
        ClubOpen=true;Paused=true;ClubPage="Главная";ClubNotice=Career.LastResult;
        SaveCareer();Revision++;
    }
    public void RestartAbandonedMediaMatch()
    {
        if(!MediaLeagueActive || !Match.Abandoned || Career?.HasRelegationChoice!=true)return;
        FinishMediaPresentation();StartRelegationMediaMatch();
    }
    void FinishMediaPresentation()
    {
        if(!MediaLeagueActive)return;
        mediaExitAllowed=true;EndRulesPreview();mediaExitAllowed=false;
        MediaLeagueActive=false;MediaLeaguePreview=false;CloseCommandWheel();CancelDirectOrder();fieldPings.Clear();
        Replay.Reset();SkipMatchIntro();accumulator=0;FieldDragSlow=false;
        QuickBroadcastOpen=false;QuickMatchRunning=false;RevealedCard=null;RevealedPack=null;
    }
    static string RelegationText(string ru,string en)=>GamePreferences.Russian?ru:en;
}