// We need to delay the bomb result by one frame so we don't award the solve bonus before the person who solved the last module is added to the Players list.
    // Delaying the log being uploaded by one frame also captures module that log after calling HandleStrike().
    public IEnumerator DelayBombResult()
    {
        yield return(null);

        LogUploader.Instance.GetBombUrl();
        IRCConnection.SendDelayedMessage(GetBombResult(), 1, SendAnalysisLink);

        foreach (var bomb in Bombs.Where(x => x != null))
        {
            Destroy(bomb.gameObject, 2.0f);
        }
        Bombs.Clear();
    }
    private void OnDisable()
    {
        GameRoom.ShowCamera();
        BombActive = false;
        EnableDisableInput();
        bool claimsEnabled = TwitchModule.ClaimsEnabled;

        TwitchModule.ClearUnsupportedModules();
        if (!claimsEnabled)
        {
            TwitchModule.ClaimsEnabled = true;
        }
        StopAllCoroutines();
        GoodPlayers.Clear();
        EvilPlayers.Clear();
        VSSetFlag        = false;
        QueueEnabled     = false;
        FindClaimEnabled = false;
        Leaderboard.Instance.BombsAttempted++;
        // ReSharper disable once DelegateSubtraction
        ParentService.GetComponent <KMGameInfo>().OnLightsChange -= OnLightsChange;
        ParentService.AddStateCoroutine(ParentService.AnimateHeaderVisibility(false));

        ParentService.AddStateCoroutine(DelayBombResult());
        if (!claimsEnabled)
        {
            IRCConnection.SendDelayedMessage("Claims have been enabled.", 1.1f);
        }

        if (ModuleCameras != null)
        {
            ModuleCameras.StartCoroutine(ModuleCameras.DisableCameras());
        }

        // Award users who maintained modules.
        var methods       = Modules.SelectMany(module => module.ScoreMethods).Where(method => method.Players.Count != 0);
        var awardedPoints = new Dictionary <string, int>();

        foreach (var player in methods.SelectMany(method => method.Players).Distinct())
        {
            int points = (methods.Sum(method => method.CalculateScore(player)) * OtherModes.ScoreMultiplier).RoundToInt();
            if (points != 0)
            {
                awardedPoints[player] = points;
                Leaderboard.Instance.AddScore(player, points);
            }
        }

        if (awardedPoints.Count > 0)
        {
            IRCConnection.SendMessage($"These players have been awarded points for managing a needy: {awardedPoints.Select(pair => $"{pair.Key} ({pair.Value})").Join(", ")}");
        }

        GameCommands.unclaimedModules = null;
        DestroyComponentHandles();

        MusicPlayer.StopAllMusic();

        GameRoom.Instance?.OnDisable();

        try
        {
            string path = Path.Combine(Application.persistentDataPath, "TwitchPlaysLastClaimed.json");
            File.WriteAllText(path, SettingsConverter.Serialize(LastClaimedModule));
        }
        catch (Exception ex)
        {
            DebugHelper.LogException(ex, "Couldn't write TwitchPlaysLastClaimed.json:");
        }
    }
    public string GetBombResult(bool lastBomb = true)
    {
        bool   hasDetonated           = false;
        bool   hasBeenSolved          = true;
        float  timeStarting           = float.MaxValue;
        float  timeRemaining          = float.MaxValue;
        string timeRemainingFormatted = "";

        foreach (TwitchBomb bomb in Bombs)
        {
            if (bomb == null)
            {
                continue;
            }

            hasDetonated  |= bomb.Bomb.HasDetonated;
            hasBeenSolved &= bomb.IsSolved;
            if (timeRemaining > bomb.CurrentTimer)
            {
                timeStarting  = bomb.BombStartingTimer;
                timeRemaining = bomb.CurrentTimer;
            }

            if (!string.IsNullOrEmpty(timeRemainingFormatted))
            {
                timeRemainingFormatted += ", " + bomb.GetFullFormattedTime;
            }
            else
            {
                timeRemainingFormatted = bomb.GetFullFormattedTime;
            }
        }

        string bombMessage = "";

        if (OtherModes.VSModeOn && (hasDetonated || hasBeenSolved))
        {
            OtherModes.Team winner = OtherModes.Team.Good;
            if (OtherModes.GetGoodHealth() == 0)
            {
                winner      = OtherModes.Team.Evil;
                bombMessage = TwitchPlaySettings.data.VersusEvilHeader;
            }
            else if (OtherModes.GetEvilHealth() == 0)
            {
                winner      = OtherModes.Team.Good;
                bombMessage = TwitchPlaySettings.data.VersusGoodHeader;
            }

            bombMessage += string.Format(TwitchPlaySettings.data.VersusEndMessage,
                                         winner == OtherModes.Team.Good ? "good" : "evil", timeRemainingFormatted);
            bombMessage += TwitchPlaySettings.GiveBonusPoints();

            if (winner == OtherModes.Team.Good)
            {
                bombMessage += TwitchPlaySettings.data.VersusGoodFooter;
            }
            else if (winner == OtherModes.Team.Evil)
            {
                bombMessage += TwitchPlaySettings.data.VersusEvilFooter;
            }

            if (lastBomb && hasBeenSolved)
            {
                Leaderboard.Instance.Success = true;
            }
        }
        else if (hasDetonated)
        {
            bombMessage = string.Format(TwitchPlaySettings.data.BombExplodedMessage, timeRemainingFormatted);
            Leaderboard.Instance.BombsExploded += Bombs.Count;
            if (!lastBomb)
            {
                return(bombMessage);
            }

            Leaderboard.Instance.Success = false;
            TwitchPlaySettings.ClearPlayerLog();
        }
        else if (hasBeenSolved)
        {
            bombMessage = string.Format(TwitchPlaySettings.data.BombDefusedMessage, timeRemainingFormatted);

            Leaderboard.Instance.BombsCleared += Bombs.Count;
            bombMessage += TwitchPlaySettings.GiveBonusPoints();

            if (lastBomb)
            {
                Leaderboard.Instance.Success = true;
            }

            if (Leaderboard.Instance.CurrentSolvers.Count != 1)
            {
                return(bombMessage);
            }

            float  elapsedTime = timeStarting - timeRemaining;
            string userName    = "";
            foreach (string uName in Leaderboard.Instance.CurrentSolvers.Keys)
            {
                userName = uName;
                break;
            }
            if (Leaderboard.Instance.CurrentSolvers[userName] == Leaderboard.RequiredSoloSolves * Bombs.Count && OtherModes.currentMode == TwitchPlaysMode.Normal)
            {
                Leaderboard.Instance.AddSoloClear(userName, elapsedTime, out float previousRecord);
                if (TwitchPlaySettings.data.EnableSoloPlayMode)
                {
                    //Still record solo information, should the defuser be the only one to actually defuse a 11 * bomb-count bomb, but display normal leaderboards instead if
                    //solo play is disabled.
                    TimeSpan elapsedTimeSpan = TimeSpan.FromSeconds(elapsedTime);
                    string   soloMessage     = string.Format(TwitchPlaySettings.data.BombSoloDefusalMessage, Leaderboard.Instance.SoloSolver.UserName, (int)elapsedTimeSpan.TotalMinutes, elapsedTimeSpan.Seconds);
                    if (elapsedTime < previousRecord)
                    {
                        TimeSpan previousTimeSpan = TimeSpan.FromSeconds(previousRecord);
                        soloMessage += string.Format(TwitchPlaySettings.data.BombSoloDefusalNewRecordMessage, (int)previousTimeSpan.TotalMinutes, previousTimeSpan.Seconds);
                    }
                    soloMessage += TwitchPlaySettings.data.BombSoloDefusalFooter;
                    IRCConnection.SendDelayedMessage(soloMessage, 1);
                }
                else
                {
                    Leaderboard.Instance.ClearSolo();
                }
            }
            else
            {
                Leaderboard.Instance.ClearSolo();
            }
        }
        else
        {
            bombMessage = string.Format(TwitchPlaySettings.data.BombAbortedMessage, timeRemainingFormatted);
            Leaderboard.Instance.Success = false;
            TwitchPlaySettings.ClearPlayerLog();
        }
        return(bombMessage);
    }