Exemplo n.º 1
0
    private void AddModuleToStack(BombComponent component, TwitchComponentHandle handle, int priority = CameraInUse)
    {
        if (component == null || handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
        {
            return;
        }

        if (!moduleItems.TryGetValue(component, out ModuleItem item))
        {
            item = new ModuleItem(component, handle, priority);
            moduleItems.Add(component, item);
        }
        else
        {
            item.priority = priority;
        }


        if (priority >= CameraPinned)
        {
            pinnedModuleStack.Push(item);
        }
        else if (priority >= CameraPrioritised)
        {
            priorityModuleStack.Push(item);
        }
        else
        {
            moduleStack.Push(item);
        }
    }
Exemplo n.º 2
0
 public static void HandleForcedSolve(MonoBehaviour bombComponent)
 {
     try
     {
         TwitchComponentHandle handle      = null;
         KMBombModule          module      = bombComponent.GetComponent <KMBombModule>();
         KMNeedyModule         needyModule = bombComponent.GetComponent <KMNeedyModule>();
         if (module != null)
         {
             handle = BombMessageResponder.Instance.ComponentHandles.Where(x => x.bombComponent.GetComponent <KMBombModule>() != null)
                      .FirstOrDefault(x => x.bombComponent.GetComponent <KMBombModule>() == module);
         }
         else if (needyModule != null)
         {
             handle = BombMessageResponder.Instance.ComponentHandles.Where(x => x.bombComponent.GetComponent <KMBombModule>() != null)
                      .FirstOrDefault(x => x.bombComponent.GetComponent <KMBombModule>() == needyModule);
         }
         if (handle != null)
         {
             HandleForcedSolve(handle);
         }
         else
         {
             CommonReflectedTypeInfo.HandlePassMethod.Invoke(bombComponent, null);
             foreach (MonoBehaviour behaviour in bombComponent.GetComponentsInChildren <MonoBehaviour>(true))
             {
                 behaviour.StopAllCoroutines();
             }
         }
     }
     catch (Exception ex)
     {
         DebugHelper.LogException(ex, "An exception occured while silently soving a module:");
     }
 }
Exemplo n.º 3
0
        public ModuleItem(BombComponent c, TwitchComponentHandle h, int p)
        {
            component = c;
            handle    = h;
            priority  = p;

            UpdateLayerData();
        }
Exemplo n.º 4
0
 protected void SolveModule(string reason = "A module is being automatically solved.", bool removeSolveBasedModules = true)
 {
     IRCConnection.Instance.SendMessage("{0}{1}", reason, removeSolveBasedModules ? " Some other modules may also be solved to prevent problems." : "");
     SolveSilently();
     if (removeSolveBasedModules)
     {
         TwitchComponentHandle.RemoveSolveBasedModules();
     }
 }
Exemplo n.º 5
0
    private Vector3 GetIdealPositionForHandle(TwitchComponentHandle thisHandle, IList bombComponents, out TwitchComponentHandle.Direction direction)
    {
        Rect handleBasicRect        = new Rect(-0.155f, -0.1f, 0.31f, 0.2f);
        Rect bombComponentBasicRect = new Rect(-0.1f, -0.1f, 0.2f, 0.2f);

        float baseUp    = (handleBasicRect.height + bombComponentBasicRect.height) * 0.55f;
        float baseRight = (handleBasicRect.width + bombComponentBasicRect.width) * 0.55f;

        Vector2 extentUp    = new Vector2(0.0f, baseUp * 0.1f);
        Vector2 extentRight = new Vector2(baseRight * 0.2f, 0.0f);

        Vector2 extentResult = Vector2.zero;

        while (true)
        {
            Rect handleRect = handleBasicRect;
            handleRect.position += extentRight;
            if (!HasOverlap(thisHandle, handleRect, bombComponentBasicRect, bombComponents))
            {
                extentResult = extentRight;
                direction    = TwitchComponentHandle.Direction.Left;
                break;
            }

            handleRect           = handleBasicRect;
            handleRect.position -= extentRight;
            if (!HasOverlap(thisHandle, handleRect, bombComponentBasicRect, bombComponents))
            {
                extentResult = -extentRight;
                direction    = TwitchComponentHandle.Direction.Right;
                break;
            }

            handleRect           = handleBasicRect;
            handleRect.position += extentUp;
            if (!HasOverlap(thisHandle, handleRect, bombComponentBasicRect, bombComponents))
            {
                extentResult = extentUp;
                direction    = TwitchComponentHandle.Direction.Down;
                break;
            }

            handleRect           = handleBasicRect;
            handleRect.position -= extentUp;
            if (!HasOverlap(thisHandle, handleRect, bombComponentBasicRect, bombComponents))
            {
                extentResult = -extentUp;
                direction    = TwitchComponentHandle.Direction.Up;
                break;
            }

            extentUp.y    += baseUp * 0.1f;
            extentRight.x += baseRight * 0.1f;
        }

        return(new Vector3(extentResult.x, 0.0f, extentResult.y));
    }
    public static void DeactivateNeedyModule(TwitchComponentHandle handle)
    {
        handle.ircConnection.SendMessage(TwitchPlaySettings.data.UnsupportedNeedyWarning);
        KMNeedyModule needyModule = handle.bombComponent.GetComponent <KMNeedyModule>();

        needyModule.OnNeedyActivation   = () => { needyModule.StartCoroutine(KeepUnsupportedNeedySilent(needyModule)); };
        needyModule.OnNeedyDeactivation = () => { needyModule.StopAllCoroutines(); needyModule.HandlePass(); };
        needyModule.OnTimerExpired      = () => { needyModule.StopAllCoroutines(); needyModule.HandlePass(); };
        needyModule.WarnAtFiveSeconds   = false;
    }
Exemplo n.º 7
0
 public static void HandleForcedSolve(TwitchComponentHandle handle)
 {
     try
     {
         if (!(handle?.Solver?.AttemptedForcedSolve ?? false) && (handle?.Solver?.HandleForcedSolve() ?? false))
         {
             handle.Solver.AttemptedForcedSolve = true;
             return;
         }
         if (!(handle?.Solver?.AttemptedForcedSolve ?? false) && handle?.Solver?.ForcedSolveMethod != null)
         {
             handle.Solver.AttemptedForcedSolve        = true;
             handle.Solver._delegatedSolveUserNickName = null;
             handle.Solver._currentUserNickName        = null;
             handle.Solver._silentlySolve = true;
             try
             {
                 handle.Solver.ForcedSolveMethod.Invoke(handle.Solver.CommandComponent, null);
             }
             catch (Exception ex)
             {
                 DebugHelper.LogException(ex, "An exception occured while using the Forced Solve handler:");
                 CommonReflectedTypeInfo.HandlePassMethod.Invoke(handle.Solver.BombComponent, null);
                 foreach (MonoBehaviour behavior in handle.bombComponent.GetComponentsInChildren <MonoBehaviour>(true))
                 {
                     behavior.StopAllCoroutines();
                 }
             }
         }
         else if (handle?.Solver != null)
         {
             handle.Solver._delegatedSolveUserNickName = null;
             handle.Solver._currentUserNickName        = null;
             handle.Solver._silentlySolve = true;
             CommonReflectedTypeInfo.HandlePassMethod.Invoke(handle.bombComponent, null);
             foreach (MonoBehaviour behavior in handle.bombComponent.GetComponentsInChildren <MonoBehaviour>(true))
             {
                 behavior.StopAllCoroutines();
             }
         }
         else if (handle != null)
         {
             CommonReflectedTypeInfo.HandlePassMethod.Invoke(handle.bombComponent, null);
             foreach (MonoBehaviour behavior in handle.bombComponent.GetComponentsInChildren <MonoBehaviour>(true))
             {
                 behavior.StopAllCoroutines();
             }
         }
     }
     catch (Exception ex)
     {
         DebugHelper.LogException(ex, "An exception occured while silently soving a module:");
     }
 }
    private bool CreateComponentHandlesForBomb(Bomb bomb)
    {
        bool foundComponents = false;

        List <BombComponent> bombComponents = bomb.BombComponents;

        var bombCommander = _bombCommanders[_bombCommanders.Count - 1];

        if (bombComponents.Count > 12 || TwitchPlaySettings.data.ForceMultiDeckerMode)
        {
            bombCommander.multiDecker = true;
        }

        foreach (BombComponent bombComponent in bombComponents)
        {
            ComponentTypeEnum componentType = bombComponent.ComponentType;

            switch (componentType)
            {
            case ComponentTypeEnum.Empty:
                continue;

            case ComponentTypeEnum.Timer:
                _bombCommanders[_bombCommanders.Count - 1].timerComponent = (TimerComponent)bombComponent;
                continue;

            default:
                foundComponents = true;
                break;
            }

            TwitchComponentHandle handle = Instantiate <TwitchComponentHandle>(twitchComponentHandlePrefab, bombComponent.transform, false);
            handle.ircConnection      = _ircConnection;
            handle.bombCommander      = bombCommander;
            handle.bombComponent      = bombComponent;
            handle.componentType      = componentType;
            handle.coroutineQueue     = _coroutineQueue;
            handle.coroutineCanceller = _coroutineCanceller;
            handle.leaderboard        = leaderboard;
            handle.bombID             = _currentBomb == -1 ? -1 : _bombCommanders.Count - 1;

            Vector3 idealOffset = handle.transform.TransformDirection(GetIdealPositionForHandle(handle, bombComponents, out handle.direction));
            handle.transform.SetParent(bombComponent.transform.parent, true);
            handle.basePosition = handle.transform.localPosition;
            handle.idealHandlePositionOffset = bombComponent.transform.parent.InverseTransformDirection(idealOffset);

            bombCommander.bombSolvableModules++;

            _componentHandles.Add(handle);
        }

        return(foundComponents);
    }
Exemplo n.º 9
0
    private bool CreateComponentHandlesForBomb(MonoBehaviour bomb)
    {
        bool foundComponents = false;

        IList bombComponents = (IList)CommonReflectedTypeInfo.BombComponentsField.GetValue(bomb);

        if (bombComponents.Count > 12)
        {
            _bombCommanders[_bombCommanders.Count - 1]._multiDecker = true;
        }

        foreach (MonoBehaviour bombComponent in bombComponents)
        {
            object            componentType     = CommonReflectedTypeInfo.ComponentTypeField.GetValue(bombComponent);
            int               componentTypeInt  = (int)Convert.ChangeType(componentType, typeof(int));
            ComponentTypeEnum componentTypeEnum = (ComponentTypeEnum)componentTypeInt;

            switch (componentTypeEnum)
            {
            case ComponentTypeEnum.Empty:
                continue;

            case ComponentTypeEnum.Timer:
                _bombCommanders[_bombCommanders.Count - 1]._timerComponent = bombComponent;
                continue;

            default:
                foundComponents = true;
                break;
            }

            TwitchComponentHandle handle = (TwitchComponentHandle)Instantiate(twitchComponentHandlePrefab, bombComponent.transform, false);
            handle.ircConnection      = _ircConnection;
            handle.bombCommander      = _bombCommanders[_bombCommanders.Count - 1];
            handle.bombComponent      = bombComponent;
            handle.componentType      = componentTypeEnum;
            handle.coroutineQueue     = _coroutineQueue;
            handle.coroutineCanceller = _coroutineCanceller;
            handle.leaderboard        = leaderboard;
            handle.bombID             = _currentBomb == -1 ? -1 : _bombCommanders.Count - 1;

            Vector3 idealOffset = handle.transform.TransformDirection(GetIdealPositionForHandle(handle, bombComponents, out handle.direction));
            handle.transform.SetParent(bombComponent.transform.parent, true);
            handle.basePosition = handle.transform.localPosition;
            handle.idealHandlePositionOffset = bombComponent.transform.parent.InverseTransformDirection(idealOffset);

            handle.bombCommander._bombSolvableModules++;

            _componentHandles.Add(handle);
        }

        return(foundComponents);
    }
Exemplo n.º 10
0
    protected void SolveModule(string reason = "A module is being automatically solved.", bool removeSolveBasedModules = true)
    {
        IRCConnection.SendMessage("{0}{1}", reason, removeSolveBasedModules ? " Some other modules may also be solved to prevent problems." : "");

        _currentUserNickName        = null;
        _delegatedSolveUserNickName = null;

        if (removeSolveBasedModules)
        {
            TwitchComponentHandle.RemoveSolveBasedModules();
        }
        CommonReflectedTypeInfo.HandlePassMethod.Invoke(BombComponent, null);
    }
Exemplo n.º 11
0
    private bool HasOverlap(TwitchComponentHandle thisHandle, Rect handleRect, Rect bombComponentBasicRect, IList bombComponents)
    {
        foreach (MonoBehaviour bombComponent in bombComponents)
        {
            Vector3 bombComponentCenter = thisHandle.transform.InverseTransformPoint(bombComponent.transform.position);

            Rect bombComponentRect = bombComponentBasicRect;
            bombComponentRect.position += new Vector2(bombComponentCenter.x, bombComponentCenter.z);

            if (bombComponentRect.Overlaps(handleRect))
            {
                return(true);
            }
        }

        return(false);
    }
    private void OnDisable()
    {
        _hideBombs = false;
        BombActive = false;
        EnableDisableInput();
        TwitchComponentHandle.ClaimedList.Clear();
        TwitchComponentHandle.ClearUnsupportedModules();
        StopAllCoroutines();
        Leaderboard.Instance.BombsAttempted++;
        parentService.GetComponent <KMGameInfo>().OnLightsChange -= OnLightsChange;

        LogUploader.Instance.Post();
        parentService.StartCoroutine(SendDelayedMessage(1.0f, GetBombResult(), SendAnalysisLink));

        moduleCameras?.gameObject.SetActive(false);

        foreach (TwitchBombHandle handle in BombHandles)
        {
            if (handle != null)
            {
                Destroy(handle.gameObject, 2.0f);
            }
        }
        BombHandles.Clear();
        BombCommanders.Clear();

        DestroyComponentHandles();

        MusicPlayer.StopAllMusic();

        GameRoom.Instance?.OnDisable();
        OtherModes.RefreshModes();

        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:");
        }
    }
Exemplo n.º 13
0
    public void AttachToModule(BombComponent component, TwitchComponentHandle handle, int priority = CameraInUse)
    {
        if (handle != null && (handle.claimed) && (priority == CameraClaimed))
        {
            priority = CameraClaimed;
        }
        int existingCamera = CurrentModulesContains(component);

        if (existingCamera > -1)
        {
            ModuleCamera cam = cameras[existingCamera];
            if (cam.priority < priority)
            {
                cam.priority        = priority;
                cam.module.priority = priority;
            }
            cam.index        = ++index;
            cam.module.index = cam.index;
            return;
        }
        ModuleCamera camera = AvailableCamera(priority);

        try
        {
            // If the camera is in use, return its module to the appropriate stack
            if ((camera.priority > CameraNotInUse) && (camera.module.component != null))
            {
                camera.module.index = camera.index;
                AddModuleToStack(camera.module.component, camera.module.handle, camera.priority);
                camera.priority = CameraNotInUse;
            }

            // Add the new module to the stack
            AddModuleToStack(component, handle, priority);

            // Refresh the camera
            camera.Refresh();
        }
        catch (Exception e)
        {
            Debug.Log(LogPrefix + "Error: " + e.Message);
        }
    }
Exemplo n.º 14
0
    private IEnumerator CheckForBomb()
    {
        TwitchComponentHandle.ResetId();

        UnityEngine.Object[] bombs;
        do
        {
            yield return(null);

            bombs = FindObjectsOfType(CommonReflectedTypeInfo.BombType);
            if (bombs.Length > 0)
            {
                yield return(new WaitForSeconds(0.1f));

                bombs = FindObjectsOfType(CommonReflectedTypeInfo.BombType);
            }

            System.Random rand = new System.Random();

            if (bombs.Length == 1)
            {
                _currentBomb = -1;
                SetBomb((MonoBehaviour)bombs[0], -1);

                if (rand.NextDouble() < specialNameProbability)
                {
                    _bombHandles[0].nameText.text = singleNames[rand.Next(0, singleNames.Length - 1)];
                }
                _coroutineQueue.AddToQueue(_bombHandles[0].OnMessageReceived(_bombHandles[0].nameText.text, "red", "!bomb hold"), -1);
            }
            else
            {
                _currentBomb = 0;
                int id = 0;
                for (var i = bombs.Length - 1; i >= 0; i--)
                {
                    SetBomb((MonoBehaviour)bombs[i], id++);
                }

                if (rand.NextDouble() < specialNameProbability)
                {
                    int    nameIndex = rand.Next(0, doubleNames.Length - 1);
                    string nameText  = null;
                    for (int i = 0; i < 2; i++)
                    {
                        nameText = doubleNames[nameIndex, i];
                        if (nameText != null)
                        {
                            _bombHandles[i].nameText.text = nameText;
                        }
                    }
                }
                else
                {
                    _bombHandles[1].nameText.text = "The Other Bomb";
                }
                _coroutineQueue.AddToQueue(_bombHandles[0].OnMessageReceived(_bombHandles[0].nameText.text, "red", "!bomb hold"), 0);
            }
        } while (bombs == null || bombs.Length == 0);

        moduleCameras = Instantiate <ModuleCameras>(moduleCamerasPrefab);
    }
    private IEnumerator CheckForBomb()
    {
        yield return(new WaitUntil(() => (SceneManager.Instance.GameplayState.Bombs != null && SceneManager.Instance.GameplayState.Bombs.Count > 0)));

        List <Bomb> bombs = SceneManager.Instance.GameplayState.Bombs;

        for (int i = 0; i < GameRoom.GameRoomTypes.Length; i++)
        {
            if (GameRoom.GameRoomTypes[i]() != null && GameRoom.CreateRooms[i](FindObjectsOfType(GameRoom.GameRoomTypes[i]()), out GameRoom.Instance))
            {
                GameRoom.Instance.InitializeBombs(bombs);
                break;
            }
        }

        try
        {
            GameRoom.Instance.InitializeBombNames();
        }
        catch (Exception ex)
        {
            DebugHelper.LogException(ex, "An exception has occured while setting the bomb names");
        }
        StartCoroutine(GameRoom.Instance.ReportBombStatus());

        try
        {
            if (GameRoom.Instance.HoldBomb)
            {
                _coroutineQueue.AddToQueue(BombHandles[0].OnMessageReceived(BombHandles[0].nameText.text, "red", "bomb hold"), _currentBomb);
            }
        }
        catch (Exception ex)
        {
            DebugHelper.LogException(ex, "An exception has occured attempting to hold the bomb.");
        }

        try
        {
            moduleCameras = Instantiate <ModuleCameras>(moduleCamerasPrefab);
        }
        catch (Exception ex)
        {
            DebugHelper.LogException(ex, "Failed to Instantiate the module camera system due to an exception:");
            moduleCameras = null;
        }
        moduleCameras?.ChangeBomb(BombCommanders[0]);

        for (int i = 0; i < 4; i++)
        {
            _notesDictionary[i] = (OtherModes.ZenModeOn && i == 3) ? TwitchPlaySettings.data.ZenModeFreeSpace : TwitchPlaySettings.data.NotesSpaceFree;
            moduleCameras?.SetNotes(i, _notesDictionary[i]);
        }

        if (EnableDisableInput())
        {
            TwitchComponentHandle.SolveUnsupportedModules(true);
        }

        while (OtherModes.ZenModeOn)
        {
            foreach (BombCommander bomb in BombCommanders)
            {
                if (bomb.timerComponent == null || bomb.timerComponent.GetRate() < 0)
                {
                    continue;
                }
                bomb.timerComponent.SetRateModifier(-bomb.timerComponent.GetRate());
            }
            yield return(null);
        }
    }
    protected override void OnMessageReceived(string userNickName, string userColorCode, string text)
    {
        Match match;
        int   index;

        if (!text.StartsWith("!") || text.Equals("!"))
        {
            return;
        }
        text = text.Substring(1);

        if (IsAuthorizedDefuser(userNickName))
        {
            if (text.RegexMatch(out match, "^notes(-?[0-9]+) (.+)$") && int.TryParse(match.Groups[1].Value, out index))
            {
                string notes = match.Groups[2].Value;

                IRCConnection.Instance.SendMessage(TwitchPlaySettings.data.NotesTaken, index--, notes);

                _notesDictionary[index] = notes;
                moduleCameras?.SetNotes(index, notes);
                return;
            }

            if (text.RegexMatch(out match, "^notes(-?[0-9]+)append (.+)", "^appendnotes(-?[0-9]+) (.+)") && int.TryParse(match.Groups[1].Value, out index))
            {
                string notes = match.Groups[2].Value;

                IRCConnection.Instance.SendMessage(TwitchPlaySettings.data.NotesAppended, index--, notes);
                if (!_notesDictionary.ContainsKey(index))
                {
                    _notesDictionary[index] = TwitchPlaySettings.data.NotesSpaceFree;
                }

                _notesDictionary[index] += " " + notes;
                moduleCameras?.AppendNotes(index, notes);
                return;
            }

            if (text.RegexMatch(out match, "^clearnotes(-?[0-9]+)$", "^notes(-?[0-9]+)clear$") && int.TryParse(match.Groups[1].Value, out index))
            {
                IRCConnection.Instance.SendMessage(TwitchPlaySettings.data.NoteSlotCleared, index--);

                _notesDictionary[index] = (OtherModes.ZenModeOn && index == 3) ? TwitchPlaySettings.data.ZenModeFreeSpace : TwitchPlaySettings.data.NotesSpaceFree;
                moduleCameras?.SetNotes(index, _notesDictionary[index]);
                return;
            }

            if (text.Equals("snooze", StringComparison.InvariantCultureIgnoreCase))
            {
                if (GameRoom.Instance is ElevatorGameRoom)
                {
                    return;                                                         //There is no alarm clock in the elevator room.
                }
                DropCurrentBomb();
                _coroutineQueue.AddToQueue(AlarmClockHoldableHandler.Instance.RespondToCommand(userNickName, "alarmclock snooze"));
                return;
            }

            if (text.Equals("modules", StringComparison.InvariantCultureIgnoreCase))
            {
                moduleCameras?.AttachToModules(ComponentHandles);
                return;
            }

            if (text.RegexMatch(out match, "^claims (.+)"))
            {
                OnMessageReceived(match.Groups[1].Value, userColorCode, "!claims");
                return;
            }

            if (text.Equals("claims", StringComparison.InvariantCultureIgnoreCase))
            {
                List <string> claimed = (
                    from handle in ComponentHandles
                    where handle.PlayerName != null && handle.PlayerName.Equals(userNickName, StringComparison.InvariantCultureIgnoreCase) && !handle.Solved
                    select string.Format(TwitchPlaySettings.data.OwnedModule, handle.Code, handle.HeaderText)).ToList();
                if (claimed.Count > 0)
                {
                    string message = string.Format(TwitchPlaySettings.data.OwnedModuleList, userNickName, string.Join(", ", claimed.ToArray(), 0, Math.Min(claimed.Count, 5)));
                    if (claimed.Count > 5)
                    {
                        message += "...";
                    }
                    IRCConnection.Instance.SendMessage(message);
                }
                else
                {
                    IRCConnection.Instance.SendMessage(TwitchPlaySettings.data.NoOwnedModules, userNickName);
                }
                return;
            }


            if (text.RegexMatch("^(?:claim ?|view ?|all ?){2,3}$"))
            {
                if (text.Contains("claim") && text.Contains("all"))
                {
                    foreach (var handle in ComponentHandles)
                    {
                        if (handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
                        {
                            continue;
                        }
                        handle.AddToClaimQueue(userNickName, text.Contains("view"));
                    }
                    return;
                }
            }

            if (text.StartsWith("claim ", StringComparison.InvariantCultureIgnoreCase))
            {
                var split = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var claim in split.Skip(1))
                {
                    TwitchComponentHandle handle = ComponentHandles.FirstOrDefault(x => x.Code.Equals(claim, StringComparison.InvariantCultureIgnoreCase));
                    if (handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
                    {
                        continue;
                    }
                    handle.AddToClaimQueue(userNickName);
                }
                return;
            }

            if (text.RegexMatch("^(unclaim|release) ?all$"))
            {
                foreach (TwitchComponentHandle handle in ComponentHandles)
                {
                    handle.RemoveFromClaimQueue(userNickName);
                }
                string[] moduleIDs = ComponentHandles.Where(x => !x.Solved && x.PlayerName != null && x.PlayerName.Equals(userNickName, StringComparison.InvariantCultureIgnoreCase))
                                     .Select(x => x.Code).ToArray();
                text = string.Format("unclaim {0}", string.Join(" ", moduleIDs));
            }

            if (text.RegexMatch(out match, "^(?:unclaim|release) (.+)"))
            {
                var split = match.Groups[1].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var claim in split)
                {
                    TwitchComponentHandle handle = ComponentHandles.FirstOrDefault(x => x.Code.Equals(claim));
                    if (handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
                    {
                        continue;
                    }
                    handle.OnMessageReceived(userNickName, userColorCode, "unclaim");
                }
                return;
            }

            if (text.Equals("unclaimed", StringComparison.InvariantCultureIgnoreCase))
            {
                IEnumerable <string> unclaimed = ComponentHandles.Where(handle => !handle.Claimed && !handle.Solved && GameRoom.Instance.IsCurrentBomb(handle.bombID)).Shuffle().Take(3)
                                                 .Select(handle => string.Format($"{handle.HeaderText} ({handle.Code})")).ToList();

                if (unclaimed.Any())
                {
                    IRCConnection.Instance.SendMessage("Unclaimed Modules: {0}", unclaimed.Join(", "));
                }
                else
                {
                    IRCConnection.Instance.SendMessage("There are no more unclaimed modules.");
                }

                return;
            }

            if (text.Equals("unsolved", StringComparison.InvariantCultureIgnoreCase))
            {
                IEnumerable <string> unsolved = ComponentHandles.Where(handle => !handle.Solved && GameRoom.Instance.IsCurrentBomb(handle.bombID)).Shuffle().Take(3)
                                                .Select(handle => string.Format("{0} ({1}) - {2}", handle.HeaderText, handle.Code,
                                                                                handle.PlayerName == null ? "Unclaimed" : "Claimed by " + handle.PlayerName)).ToList();
                if (unsolved.Any())
                {
                    IRCConnection.Instance.SendMessage("Unsolved Modules: {0}", unsolved.Join(", "));
                }
                else
                {
                    IRCConnection.Instance.SendMessage("There are no unsolved modules, something went wrong as this message should never be displayed.");                  //this should never happen
                }
                return;
            }

            if (text.RegexMatch(out match, "^(?:find|search) (.+)"))
            {
                string[] queries = match.Groups[1].Value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);

                foreach (string query in queries)
                {
                    string trimmed = query.Trim();
                    IEnumerable <string> modules = ComponentHandles.Where(handle => handle.HeaderText.ContainsIgnoreCase(trimmed) && GameRoom.Instance.IsCurrentBomb(handle.bombID))
                                                   .OrderByDescending(handle => handle.HeaderText.EqualsIgnoreCase(trimmed)).ThenBy(handle => handle.Solved).ThenBy(handle => handle.PlayerName != null).Take(3)
                                                   .Select(handle => string.Format("{0} ({1}) - {2}", handle.HeaderText, handle.Code,
                                                                                   handle.Solved ? "Solved" : (handle.PlayerName == null ? "Unclaimed" : "Claimed by " + handle.PlayerName)
                                                                                   )).ToList();

                    if (modules.Any())
                    {
                        IRCConnection.Instance.SendMessage("Modules: {0}", modules.Join(", "));
                    }
                    else
                    {
                        IRCConnection.Instance.SendMessage($"Couldn't find any modules containing \"{trimmed}\".");
                    }
                }

                return;
            }

            if (text.RegexMatch(out match, "^(?:findplayer|playerfind|searchplayer|playersearch) (.+)"))
            {
                string[] queries = match.Groups[1].Value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Distinct().ToArray();
                foreach (string query in queries)
                {
                    string trimmed = query.Trim();
                    IEnumerable <TwitchComponentHandle> modules = ComponentHandles.Where(handle => handle.HeaderText.ContainsIgnoreCase(trimmed) && GameRoom.Instance.IsCurrentBomb(handle.bombID))
                                                                  .OrderByDescending(handle => handle.HeaderText.EqualsIgnoreCase(trimmed)).ThenBy(handle => handle.Solved).ToList();
                    IEnumerable <string> playerModules = modules.Where(handle => handle.PlayerName != null).OrderByDescending(handle => handle.HeaderText.EqualsIgnoreCase(trimmed))
                                                         .Select(handle => string.Format($"{handle.HeaderText} ({handle.Code}) - Claimed by {handle.PlayerName}")).ToList();
                    if (modules.Any())
                    {
                        if (playerModules.Any())
                        {
                            IRCConnection.Instance.SendMessage("Modules: {0}", playerModules.Join(", "));
                        }
                        else
                        {
                            IRCConnection.Instance.SendMessage("None of the specified modules are claimed/have been solved.");
                        }
                    }
                    else
                    {
                        IRCConnection.Instance.SendMessage($"Could not find any modules containing \"{trimmed}\".");
                    }
                }
            }

            if (text.RegexMatch(out match, "^(claim ?(?:any|van|mod) ?(?:view)?|view ?claim ?(?:any|van|mod))"))
            {
                var vanilla = match.Groups[1].Value.Contains("van");
                var modded  = match.Groups[1].Value.Contains("mod");
                var view    = match.Groups[1].Value.Contains("view");
                var avoid   = new[] { "Souvenir", "Forget Me Not", "Turn The Key", "Turn The Keys", "The Swan", "Forget Everything" };

                var unclaimed = ComponentHandles
                                .Where(handle => (vanilla ? !handle.IsMod : modded ? handle.IsMod : true) && !handle.Claimed && !handle.Solved && !avoid.Contains(handle.HeaderText) && GameRoom.Instance.IsCurrentBomb(handle.bombID))
                                .Shuffle().FirstOrDefault();

                if (unclaimed != null)
                {
                    text = unclaimed.Code + (view ? " claimview" : " claim");
                }
                else
                {
                    IRCConnection.Instance.SendMessage(string.Format("There are no more unclaimed{0} modules.", vanilla ? " vanilla" : modded ? " modded" : null));
                }
            }

            if (text.RegexMatch(out match, "^(?:findsolved|solvedfind|searchsolved|solvedsearch) (.+)"))
            {
                string[] queries = match.Groups[1].Value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Distinct().ToArray();
                foreach (string query in queries)
                {
                    string trimmed = query.Trim();
                    IEnumerable <BombCommander>         commanders = BombCommanders.Where(handle => handle.SolvedModules.Keys.ToArray().Any(x => x.ContainsIgnoreCase(trimmed))).ToList();
                    IEnumerable <TwitchComponentHandle> modules    = commanders.SelectMany(x => x.SolvedModules.Where(y => y.Key.ContainsIgnoreCase(trimmed)))
                                                                     .OrderByDescending(x => x.Key.EqualsIgnoreCase(trimmed)).SelectMany(x => x.Value).ToList();
                    IEnumerable <string> playerModules = modules.Where(handle => handle.PlayerName != null)
                                                         .Select(handle => string.Format($"{handle.HeaderText} ({handle.Code}) - Claimed by {handle.PlayerName}", handle.HeaderText, handle.Code, "Claimed by " + handle.PlayerName)).ToList();
                    if (commanders.Any())
                    {
                        if (playerModules.Any())
                        {
                            IRCConnection.Instance.SendMessage("Modules: {0}", playerModules.Join(", "));
                        }
                        else
                        {
                            IRCConnection.Instance.SendMessage("None of the specified modules have been solved.");
                        }
                    }
                    else
                    {
                        IRCConnection.Instance.SendMessage($"Could not find any modules containing \"{trimmed}\".");
                    }
                }
            }

            if (text.RegexMatch(out match, "^((?:(?:find|search)|claim|view|all){2,4}) (.+)"))
            {
                bool validFind  = match.Groups[1].Value.Contains("find") || match.Groups[1].Value.Contains("search");
                bool validClaim = match.Groups[1].Value.Contains("claim");
                if (!validFind || !validClaim)
                {
                    return;
                }

                bool validView = match.Groups[1].Value.Contains("view");
                bool validAll  = match.Groups[1].Value.Contains("all");

                string[] queries = match.Groups[2].Value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
                int      counter = 0;

                foreach (string query in queries)
                {
                    if (counter == 2)
                    {
                        return;
                    }
                    string trimmed = query.Trim();
                    IEnumerable <string> modules = ComponentHandles.Where(handle => handle.HeaderText.ContainsIgnoreCase(trimmed) && GameRoom.Instance.IsCurrentBomb(handle.bombID) && !handle.Solved && !handle.Claimed)
                                                   .OrderByDescending(handle => handle.HeaderText.EqualsIgnoreCase(trimmed)).Select(handle => $"{handle.Code}").ToList();
                    if (modules.Any())
                    {
                        if (!validAll)
                        {
                            modules = modules.Take(1);
                        }
                        foreach (string module in modules)
                        {
                            TwitchComponentHandle handle = ComponentHandles.FirstOrDefault(x => x.Code.Equals(module));
                            if (handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
                            {
                                continue;
                            }
                            handle.AddToClaimQueue(userNickName, validView);
                        }
                        if (validAll)
                        {
                            counter++;
                        }
                    }
                    else
                    {
                        IRCConnection.Instance.SendMessage($"Couldn't find any modules containing \"{trimmed}\".");
                    }
                }
                return;
            }

            if (text.Equals("newbomb", StringComparison.InvariantCultureIgnoreCase) && OtherModes.ZenModeOn)
            {
                TwitchPlaySettings.SetRewardBonus(0);
                foreach (var handle in ComponentHandles.Where(x => GameRoom.Instance.IsCurrentBomb(x.bombID)))
                {
                    if (!handle.Solved)
                    {
                        handle.SolveSilently();
                    }
                }
                return;
            }
        }

        if (text.RegexMatch(out match, "^notes(-?[0-9]+)$") && int.TryParse(match.Groups[1].Value, out index))
        {
            if (!_notesDictionary.ContainsKey(index - 1))
            {
                _notesDictionary[index - 1] = TwitchPlaySettings.data.NotesSpaceFree;
            }
            IRCConnection.Instance.SendMessage(TwitchPlaySettings.data.Notes, index, _notesDictionary[index - 1]);
            return;
        }

        switch (UserAccess.HighestAccessLevel(userNickName))
        {
        case AccessLevel.Streamer:
        case AccessLevel.SuperUser:
            if (text.RegexMatch(out match, @"^setmultiplier ([0-9]+(?:\.[0-9]+)*)$"))
            {
                OtherModes.SetMultiplier(float.Parse(match.Groups[1].Value));
                return;
            }

            if (text.Equals("solvebomb", StringComparison.InvariantCultureIgnoreCase))
            {
                foreach (var handle in ComponentHandles.Where(x => GameRoom.Instance.IsCurrentBomb(x.bombID)))
                {
                    if (!handle.Solved)
                    {
                        handle.SolveSilently();
                    }
                }
                return;
            }
            goto case AccessLevel.Admin;

        case AccessLevel.Admin:
            if (text.Equals("enablecamerawall", StringComparison.InvariantCultureIgnoreCase) || text.Equals("enablemodulewall", StringComparison.InvariantCultureIgnoreCase))
            {
                moduleCameras.EnableWallOfCameras();
                StartCoroutine(HideBombs());
            }

            if (text.Equals("disablecamerawall", StringComparison.InvariantCultureIgnoreCase) || text.Equals("disablemodulewall", StringComparison.InvariantCultureIgnoreCase))
            {
                moduleCameras.DisableWallOfCameras();
                _hideBombs = false;
            }
            goto case AccessLevel.Mod;

        case AccessLevel.Mod:
            if (text.RegexMatch(out match, @"^assign (\S+) (.+)"))
            {
                var split = match.Groups[2].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var assign in split)
                {
                    TwitchComponentHandle handle = ComponentHandles.FirstOrDefault(x => x.Code.Equals(assign));
                    if (handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
                    {
                        continue;
                    }
                    handle.OnMessageReceived(userNickName, userColorCode, string.Format("assign {0}", match.Groups[1].Value));
                }
                return;
            }

            if (text.RegexMatch("^bot ?unclaim( ?all)?$"))
            {
                userNickName = IRCConnection.Instance.UserNickName;
                foreach (TwitchComponentHandle handle in ComponentHandles)
                {
                    handle.RemoveFromClaimQueue(userNickName);
                }
                string[] moduleIDs = ComponentHandles.Where(x => !x.Solved && x.PlayerName != null && x.PlayerName.Equals(userNickName, StringComparison.InvariantCultureIgnoreCase))
                                     .Select(x => x.Code).ToArray();
                foreach (var claim in moduleIDs)
                {
                    TwitchComponentHandle handle = ComponentHandles.FirstOrDefault(x => x.Code.Equals(claim));
                    if (handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
                    {
                        continue;
                    }
                    handle.OnMessageReceived(userNickName, userColorCode, "unclaim");
                }
                return;
            }

            if (text.Equals("filledgework", StringComparison.InvariantCultureIgnoreCase))
            {
                foreach (var commander in BombCommanders)
                {
                    commander.FillEdgework(_currentBomb != commander.twitchBombHandle.bombID);
                }
                return;
            }
            break;
        }

        GameRoom.Instance.RefreshBombID(ref _currentBomb);

        if (_currentBomb > -1)
        {
            //Check for !bomb messages, and pass them off to the currently held bomb.
            if (text.RegexMatch(out match, "^bomb (.+)"))
            {
                string internalCommand = match.Groups[1].Value;
                text = string.Format("bomb{0} {1}", _currentBomb + 1, internalCommand);
            }

            if (text.RegexMatch(out match, "^edgework$"))
            {
                text = string.Format("edgework{0}", _currentBomb + 1);
            }
            else
            {
                if (text.RegexMatch(out match, "^edgework (.+)"))
                {
                    string internalCommand = match.Groups[1].Value;
                    text = string.Format("edgework{0} {1}", _currentBomb + 1, internalCommand);
                }
            }
        }

        foreach (TwitchBombHandle handle in BombHandles)
        {
            if (handle == null)
            {
                continue;
            }
            IEnumerator onMessageReceived = handle.OnMessageReceived(userNickName, userColorCode, text);
            if (onMessageReceived == null)
            {
                continue;
            }

            if (_currentBomb != handle.bombID)
            {
                if (!GameRoom.Instance.IsCurrentBomb(handle.bombID))
                {
                    continue;
                }

                _coroutineQueue.AddToQueue(BombHandles[_currentBomb].HideMainUIWindow(), handle.bombID);
                _coroutineQueue.AddToQueue(handle.ShowMainUIWindow(), handle.bombID);
                _coroutineQueue.AddToQueue(BombCommanders[_currentBomb].LetGoBomb(), handle.bombID);

                _currentBomb = handle.bombID;
            }
            _coroutineQueue.AddToQueue(onMessageReceived, handle.bombID);
        }

        foreach (TwitchComponentHandle componentHandle in ComponentHandles)
        {
            if (!GameRoom.Instance.IsCurrentBomb(componentHandle.bombID))
            {
                continue;
            }
            if (!text.StartsWith(componentHandle.Code + " "))
            {
                continue;
            }
            IEnumerator onMessageReceived = componentHandle.OnMessageReceived(userNickName, userColorCode, text.Substring(componentHandle.Code.Length + 1));
            if (onMessageReceived == null)
            {
                continue;
            }

            if (_currentBomb != componentHandle.bombID)
            {
                _coroutineQueue.AddToQueue(BombHandles[_currentBomb].HideMainUIWindow(), componentHandle.bombID);
                _coroutineQueue.AddToQueue(BombHandles[componentHandle.bombID].ShowMainUIWindow(), componentHandle.bombID);
                _coroutineQueue.AddToQueue(BombCommanders[_currentBomb].LetGoBomb(), componentHandle.bombID);
                _currentBomb = componentHandle.bombID;
            }
            _coroutineQueue.AddToQueue(onMessageReceived, componentHandle.bombID);
        }

        if (TwitchPlaySettings.data.BombCustomMessages.ContainsKey(text.ToLowerInvariant()))
        {
            IRCConnection.Instance.SendMessage(TwitchPlaySettings.data.BombCustomMessages[text.ToLowerInvariant()]);
        }
    }
    private void OnDisable()
    {
        BombActive = false;
        EnableDisableInput();
        TwitchComponentHandle.ClaimedList.Clear();
        TwitchComponentHandle.ClearUnsupportedModules();
        StopAllCoroutines();
        leaderboard.BombsAttempted++;
        string bombMessage = null;

        bool HasDetonated           = false;
        bool HasBeenSolved          = true;
        var  timeStarting           = float.MaxValue;
        var  timeRemaining          = float.MaxValue;
        var  timeRemainingFormatted = "";

        TwitchPlaysService.logUploader.Post();

        foreach (var commander in _bombCommanders)
        {
            HasDetonated  |= commander.Bomb.HasDetonated;
            HasBeenSolved &= commander.IsSolved;
            if (timeRemaining > commander.CurrentTimer)
            {
                timeStarting  = commander.bombStartingTimer;
                timeRemaining = commander.CurrentTimer;
            }

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

        if (HasDetonated)
        {
            bombMessage = string.Format(TwitchPlaySettings.data.BombExplodedMessage, timeRemainingFormatted);
            leaderboard.BombsExploded += _bombCommanders.Count;
            leaderboard.Success        = false;
            TwitchPlaySettings.ClearPlayerLog();
        }
        else if (HasBeenSolved)
        {
            bombMessage  = string.Format(TwitchPlaySettings.data.BombDefusedMessage, timeRemainingFormatted);
            bombMessage += TwitchPlaySettings.GiveBonusPoints(leaderboard);
            leaderboard.BombsCleared += _bombCommanders.Count;
            leaderboard.Success       = true;
            if (leaderboard.CurrentSolvers.Count == 1)
            {
                float  previousRecord = 0.0f;
                float  elapsedTime    = timeStarting - timeRemaining;
                string userName       = "";
                foreach (string uName in leaderboard.CurrentSolvers.Keys)
                {
                    userName = uName;
                    break;
                }
                if (leaderboard.CurrentSolvers[userName] == (Leaderboard.RequiredSoloSolves * _bombCommanders.Count))
                {
                    leaderboard.AddSoloClear(userName, elapsedTime, out 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.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;
                        parentService.StartCoroutine(SendDelayedMessage(1.0f, soloMessage));
                    }
                    else
                    {
                        leaderboard.ClearSolo();
                    }
                }
                else
                {
                    leaderboard.ClearSolo();
                }
            }
        }
        else
        {
            bombMessage         = string.Format(TwitchPlaySettings.data.BombAbortedMessage, timeRemainingFormatted);
            leaderboard.Success = false;
            TwitchPlaySettings.ClearPlayerLog();
        }
        parentService.StartCoroutine(SendDelayedMessage(1.0f, bombMessage, SendAnalysisLink));

        moduleCameras?.gameObject.SetActive(false);

        foreach (TwitchBombHandle handle in _bombHandles)
        {
            if (handle != null)
            {
                Destroy(handle.gameObject, 2.0f);
            }
        }
        _bombHandles.Clear();
        _bombCommanders.Clear();

        if (_componentHandles != null)
        {
            foreach (TwitchComponentHandle handle in _componentHandles)
            {
                Destroy(handle.gameObject, 2.0f);
            }
            _componentHandles.Clear();
        }

        MusicPlayer.StopAllMusic();
    }
    public bool CreateComponentHandlesForBomb(Bomb bomb)
    {
        string[] keyModules =
        {
            "SouvenirModule", "MemoryV2", "TurnTheKey", "TurnTheKeyAdvanced", "theSwan", "HexiEvilFMN"
        };
        bool foundComponents = false;

        List <BombComponent> bombComponents = bomb.BombComponents.Shuffle().ToList();

        var bombCommander = BombCommanders[BombCommanders.Count - 1];

        foreach (BombComponent bombComponent in bombComponents)
        {
            ComponentTypeEnum componentType = bombComponent.ComponentType;
            bool   keyModule  = false;
            string moduleName = "";

            switch (componentType)
            {
            case ComponentTypeEnum.Empty:
                continue;

            case ComponentTypeEnum.Timer:
                BombCommanders[BombCommanders.Count - 1].timerComponent = (TimerComponent)bombComponent;
                continue;

            case ComponentTypeEnum.NeedyCapacitor:
            case ComponentTypeEnum.NeedyKnob:
            case ComponentTypeEnum.NeedyVentGas:
            case ComponentTypeEnum.NeedyMod:
                moduleName      = bombComponent.GetModuleDisplayName();
                keyModule       = true;
                foundComponents = true;
                break;

            case ComponentTypeEnum.Mod:
                KMBombModule module = bombComponent.GetComponent <KMBombModule>();
                keyModule = keyModules.Contains(module.ModuleType);
                goto default;

            default:
                moduleName = bombComponent.GetModuleDisplayName();
                bombCommander.bombSolvableModules++;
                foundComponents = true;
                break;
            }

            if (!bombCommander.SolvedModules.ContainsKey(moduleName))
            {
                bombCommander.SolvedModules[moduleName] = new List <TwitchComponentHandle>();
            }

            TwitchComponentHandle handle = Instantiate <TwitchComponentHandle>(twitchComponentHandlePrefab, bombComponent.transform, false);
            handle.bombCommander  = bombCommander;
            handle.bombComponent  = bombComponent;
            handle.componentType  = componentType;
            handle.coroutineQueue = _coroutineQueue;
            handle.bombID         = _currentBomb == -1 ? -1 : BombCommanders.Count - 1;
            handle.IsKey          = keyModule;

            handle.transform.SetParent(bombComponent.transform.parent, true);
            handle.basePosition = handle.transform.localPosition;

            ComponentHandles.Add(handle);
        }

        return(foundComponents);
    }
    protected override void OnMessageReceived(string userNickName, string userColorCode, string text)
    {
        if (text.EqualsAny("!notes1", "!notes2", "!notes3", "!notes4"))
        {
            int index = "1234".IndexOf(text.Substring(6, 1), StringComparison.Ordinal);
            _ircConnection.SendMessage(TwitchPlaySettings.data.Notes, index + 1, _notes[index]);
            return;
        }

        if (text.StartsWith("!notes1 ", StringComparison.InvariantCultureIgnoreCase) || text.StartsWith("!notes2 ", StringComparison.InvariantCultureIgnoreCase) ||
            text.StartsWith("!notes3 ", StringComparison.InvariantCultureIgnoreCase) || text.StartsWith("!notes4 ", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }
            int    index = "1234".IndexOf(text.Substring(6, 1), StringComparison.Ordinal);
            string notes = text.Substring(8);
            if (notes == "")
            {
                return;
            }

            _ircConnection.SendMessage(TwitchPlaySettings.data.NotesTaken, index + 1, notes);

            _notes[index] = notes;
            moduleCameras?.SetNotes(index, notes);
            return;
        }

        if (text.StartsWith("!appendnotes1 ", StringComparison.InvariantCultureIgnoreCase) || text.StartsWith("!appendnotes2 ", StringComparison.InvariantCultureIgnoreCase) ||
            text.StartsWith("!appendnotes3 ", StringComparison.InvariantCultureIgnoreCase) || text.StartsWith("!appendnotes4 ", StringComparison.InvariantCultureIgnoreCase))
        {
            text = text.Substring(0, 1) + text.Substring(7, 6) + text.Substring(1, 6) + text.Substring(13);
        }

        if (text.StartsWith("!notes1append ", StringComparison.InvariantCultureIgnoreCase) || text.StartsWith("!notes2append ", StringComparison.InvariantCultureIgnoreCase) ||
            text.StartsWith("!notes3append ", StringComparison.InvariantCultureIgnoreCase) || text.StartsWith("!notes4append ", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }
            int    index = "1234".IndexOf(text.Substring(6, 1), StringComparison.Ordinal);
            string notes = text.Substring(14);
            if (notes == "")
            {
                return;
            }

            _ircConnection.SendMessage(TwitchPlaySettings.data.NotesAppended, index + 1, notes);

            _notes[index] += " " + notes;
            moduleCameras?.AppendNotes(index, notes);
            return;
        }

        if (text.EqualsAny("!clearnotes1", "!clearnotes2", "!clearnotes3", "!clearnotes4"))
        {
            text = text.Substring(0, 1) + text.Substring(6, 6) + text.Substring(1, 5);
        }

        if (text.EqualsAny("!notes1clear", "!notes2clear", "!notes3clear", "!notes4clear"))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }
            int index = "1234".IndexOf(text.Substring(6, 1), StringComparison.Ordinal);
            _notes[index] = TwitchPlaySettings.data.NotesSpaceFree;
            _ircConnection.SendMessage(TwitchPlaySettings.data.NoteSlotCleared, index + 1);

            moduleCameras?.SetNotes(index, TwitchPlaySettings.data.NotesSpaceFree);
            return;
        }

        if (text.Equals("!snooze", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }
            if (TwitchPlaySettings.data.AllowSnoozeOnly)
            {
                alarmClock.TurnOff();
            }
            else
            {
                alarmClock.ButtonDown(0);
            }
            return;
        }

        if (text.Equals("!stop", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName, true))
            {
                return;
            }
            _currentBomb = _coroutineQueue.CurrentBombID;
            return;
        }

        if (text.Equals("!modules", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }
            moduleCameras?.AttachToModules(_componentHandles);
            return;
        }

        if (text.StartsWith("!claims ", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }
            userNickName = text.Substring(8);
            text         = "!claims";
            if (userNickName == "")
            {
                return;
            }
        }

        if (text.Equals("!claims", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }
            List <string> claimed = (from handle in _componentHandles where handle.PlayerName != null && handle.PlayerName.Equals(userNickName, StringComparison.InvariantCultureIgnoreCase) && !handle.Solved select string.Format(TwitchPlaySettings.data.OwnedModule, handle.idText.text.Replace("!", ""), handle.headerText.text)).ToList();
            if (claimed.Count > 0)
            {
                string message = string.Format(TwitchPlaySettings.data.OwnedModuleList, userNickName, string.Join(", ", claimed.ToArray(), 0, Math.Min(claimed.Count, 5)));
                if (claimed.Count > 5)
                {
                    message += "...";
                }
                _ircConnection.SendMessage(message);
            }
            else
            {
                _ircConnection.SendMessage(TwitchPlaySettings.data.NoOwnedModules, userNickName);
            }
            return;
        }

        if (text.StartsWith("!claim ", StringComparison.InvariantCultureIgnoreCase))
        {
            var split = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var claim in split.Skip(1))
            {
                TwitchComponentHandle handle = _componentHandles.FirstOrDefault(x => x.Code.Equals(claim));
                if (handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
                {
                    continue;
                }
                handle.OnMessageReceived(userNickName, userColorCode, string.Format("!{0} claim", claim));
            }
            return;
        }

        if (text.EqualsAny("!unclaim all", "!release all", "!unclaimall", "!releaseall"))
        {
            string[] moduleIDs = _componentHandles.Where(x => x.PlayerName != null && x.PlayerName.Equals(userNickName, StringComparison.InvariantCultureIgnoreCase))
                                 .Select(x => x.Code).ToArray();
            text = string.Format("!unclaim {0}", string.Join(" ", moduleIDs));
        }

        if (text.StartsWith("!unclaim ", StringComparison.InvariantCultureIgnoreCase) || text.StartsWith("!release ", StringComparison.InvariantCultureIgnoreCase))
        {
            var split = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var claim in split.Skip(1))
            {
                TwitchComponentHandle handle = _componentHandles.FirstOrDefault(x => x.Code.Equals(claim));
                if (handle == null || !GameRoom.Instance.IsCurrentBomb(handle.bombID))
                {
                    continue;
                }
                handle.OnMessageReceived(userNickName, userColorCode, string.Format("!{0} unclaim", claim));
            }
            return;
        }



        if (text.Equals("!unclaimed", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }

            IEnumerable <string> unclaimed = _componentHandles.Where(handle => !handle.claimed && !handle.Solved && GameRoom.Instance.IsCurrentBomb(handle.bombID)).Shuffle().Take(3)
                                             .Select(handle => string.Format("{0} ({1})", handle.headerText.text, handle.Code)).ToList();

            if (unclaimed.Any())
            {
                _ircConnection.SendMessage("Unclaimed Modules: {0}", unclaimed.Join(", "));
            }
            else
            {
                _ircConnection.SendMessage("There are no more unclaimed modules.");
            }

            return;
        }

        if (text.StartsWith("!find ", StringComparison.InvariantCultureIgnoreCase) || text.StartsWith("!search ", StringComparison.InvariantCultureIgnoreCase))
        {
            if (!IsAuthorizedDefuser(userNickName))
            {
                return;
            }

            var    split = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            string query = split.Skip(1).Join(" ");
            IEnumerable <string> modules = _componentHandles.Where(handle => handle.headerText.text.ContainsIgnoreCase(query) && GameRoom.Instance.IsCurrentBomb(handle.bombID))
                                           .OrderByDescending(handle => handle.headerText.text.EqualsIgnoreCase(query)).ThenBy(handle => handle.Solved).ThenBy(handle => handle.PlayerName != null).Take(3)
                                           .Select(handle => string.Format("{0} ({1}) - {2}", handle.headerText.text, handle.Code,
                                                                           handle.Solved ? "Solved" : (handle.PlayerName == null ? "Unclaimed" : "Claimed by " + handle.PlayerName)
                                                                           )).ToList();

            if (modules.Any())
            {
                _ircConnection.SendMessage("Modules: {0}", modules.Join(", "));
            }
            else
            {
                _ircConnection.SendMessage("Couldn't find any modules containing \"{0}\".", query);
            }

            return;
        }

        if (text.Equals("!filledgework", StringComparison.InvariantCultureIgnoreCase) && UserAccess.HasAccess(userNickName, AccessLevel.Mod, true))
        {
            foreach (var commander in _bombCommanders)
            {
                commander.FillEdgework(_currentBomb != commander.twitchBombHandle.bombID);
            }
            return;
        }

        if (text.StartsWith("!setmultiplier", StringComparison.InvariantCultureIgnoreCase) && UserAccess.HasAccess(userNickName, AccessLevel.SuperUser, true))
        {
            float tempNumber = float.Parse(text.Substring(15));
            OtherModes.setMultiplier(tempNumber);
        }

        if (text.Equals("!solvebomb", StringComparison.InvariantCultureIgnoreCase) && UserAccess.HasAccess(userNickName, AccessLevel.SuperUser, true))
        {
            foreach (var handle in _componentHandles.Where(x => GameRoom.Instance.IsCurrentBomb(x.bombID)))
            {
                if (!handle.Solved)
                {
                    handle.SolveSilently();
                }
            }
            return;
        }

        GameRoom.Instance.RefreshBombID(ref _currentBomb);

        if (_currentBomb > -1)
        {
            //Check for !bomb messages, and pass them off to the currently held bomb.
            Match match = Regex.Match(text, "^!bomb (.+)", RegexOptions.IgnoreCase);
            if (match.Success)
            {
                string internalCommand = match.Groups[1].Value;
                text = string.Format("!bomb{0} {1}", _currentBomb + 1, internalCommand);
            }

            match = Regex.Match(text, "^!edgework$");
            if (match.Success)
            {
                text = string.Format("!edgework{0}", _currentBomb + 1);
            }
            else
            {
                match = Regex.Match(text, "^!edgework (.+)");
                if (match.Success)
                {
                    string internalCommand = match.Groups[1].Value;
                    text = string.Format("!edgework{0} {1}", _currentBomb + 1, internalCommand);
                }
            }
        }

        foreach (TwitchBombHandle handle in _bombHandles)
        {
            if (handle == null)
            {
                continue;
            }
            IEnumerator onMessageReceived = handle.OnMessageReceived(userNickName, userColorCode, text);
            if (onMessageReceived == null)
            {
                continue;
            }

            if (_currentBomb != handle.bombID)
            {
                if (!GameRoom.Instance.IsCurrentBomb(handle.bombID))
                {
                    continue;
                }

                _coroutineQueue.AddToQueue(_bombHandles[_currentBomb].HideMainUIWindow(), handle.bombID);
                _coroutineQueue.AddToQueue(handle.ShowMainUIWindow(), handle.bombID);
                _coroutineQueue.AddToQueue(_bombCommanders[_currentBomb].LetGoBomb(), handle.bombID);

                _currentBomb = handle.bombID;
            }
            _coroutineQueue.AddToQueue(onMessageReceived, handle.bombID);
        }

        foreach (TwitchComponentHandle componentHandle in _componentHandles)
        {
            if (!GameRoom.Instance.IsCurrentBomb(componentHandle.bombID))
            {
                continue;
            }
            IEnumerator onMessageReceived = componentHandle.OnMessageReceived(userNickName, userColorCode, text);
            if (onMessageReceived == null)
            {
                continue;
            }

            if (_currentBomb != componentHandle.bombID)
            {
                _coroutineQueue.AddToQueue(_bombHandles[_currentBomb].HideMainUIWindow(), componentHandle.bombID);
                _coroutineQueue.AddToQueue(_bombHandles[componentHandle.bombID].ShowMainUIWindow(), componentHandle.bombID);
                _coroutineQueue.AddToQueue(_bombCommanders[_currentBomb].LetGoBomb(), componentHandle.bombID);
                _currentBomb = componentHandle.bombID;
            }
            _coroutineQueue.AddToQueue(onMessageReceived, componentHandle.bombID);
        }
    }
    private IEnumerator CheckForBomb()
    {
        TwitchComponentHandle.ResetId();

        Bomb[] bombs;
        do
        {
            yield return(null);

            bombs = FindObjectsOfType <Bomb>();
            if (bombs.Length > 0)
            {
                yield return(new WaitForSeconds(0.1f));

                bombs = FindObjectsOfType <Bomb>();
            }

            for (int i = 0; i < GameRoom.GameRoomTypes.Length; i++)
            {
                if (GameRoom.GameRoomTypes[i]() != null && GameRoom.CreateRooms[i](FindObjectsOfType(GameRoom.GameRoomTypes[i]()), out GameRoom.Instance))
                {
                    break;
                }
            }
            _currentBomb = bombs.Length == 1 ? -1 : 0;
            for (int i = 0; i < bombs.Length; i++)
            {
                SetBomb(bombs[i], _currentBomb == -1 ? -1 : i);
            }
            GameRoom.Instance.InitializeBombNames(_bombHandles);
            StartCoroutine(GameRoom.Instance.ReportBombStatus(_bombHandles));

            if (GameRoom.Instance.HoldBomb)
            {
                _coroutineQueue.AddToQueue(_bombHandles[0].OnMessageReceived(_bombHandles[0].nameText.text, "red", "!bomb hold"), _currentBomb);
            }
        } while (bombs.Length == 0);

        AlarmClock[] clocks;
        do
        {
            yield return(null);

            clocks = FindObjectsOfType <AlarmClock>();
        } while (clocks == null || clocks.Length == 0);
        alarmClock = clocks[0];

        try
        {
            moduleCameras = Instantiate <ModuleCameras>(moduleCamerasPrefab);
        }
        catch (Exception ex)
        {
            DebugHelper.LogException(ex, "Failed to Instantiate the module Camera system due to an Exception: ");
            moduleCameras = null;
        }

        for (int i = 0; i < 4; i++)
        {
            _notes[i] = TwitchPlaySettings.data.NotesSpaceFree;
            moduleCameras?.SetNotes(i, TwitchPlaySettings.data.NotesSpaceFree);
        }

        if (EnableDisableInput())
        {
            TwitchComponentHandle.SolveUnsupportedModules();
        }
    }