private static void SetSetting(FloatingHoldable holdable, bool on, string user, bool isWhisper, Func <FreeplaySettings, bool> getCurrent, Func <FreeplayDevice, ToggleSwitch> toggle, bool settingAllowed, string disabledMessage) { if (settingAllowed || !on || UserAccess.HasAccess(user, AccessLevel.Admin, true) || TwitchPlaySettings.data.AnarchyMode) { var device = holdable.GetComponent <FreeplayDevice>(); if (on != getCurrent(device.CurrentSettings)) { toggle(device).GetComponent <Selectable>().Trigger(); } } else { IRCConnection.SendMessage(string.Format(disabledMessage, user), user, !isWhisper); } }
public IEnumerator Focus(Selectable selectable, float focusDistance, bool frontFace) { IEnumerator holdCoroutine = HoldBomb(frontFace); while (holdCoroutine.MoveNext()) { yield return(holdCoroutine.Current); } float focusTime = FloatingHoldable.FocusTime; FloatingHoldable.Focus(selectable.transform, focusDistance, false, false, focusTime); selectable.HandleSelect(false); selectable.HandleInteract(); }
/// <summary> /// Unity event. /// </summary> private void Awake() { Ended = false; InternalBomb = GetComponent <Bomb>(); Timer = InternalBomb.GetTimer(); _holdable = GetComponentInChildren <FloatingHoldable>(); _selectableArea = GetComponentInChildren <SelectableArea>(); Selectable = GetComponent <Selectable>(); ChangeTimerVisibility(false); SetSelectableLayer(false); BombEvents.OnBombSolved += OnAnyBombSolved; BombEvents.OnBombDetonated += OnAnyBombDetonated; }
protected HoldableHandler(KMHoldableCommander commander, FloatingHoldable holdable) { HoldableCommander = commander; Holdable = holdable; if (!BombMessageResponder.BombActive) { return; } KMGameCommands gameCommands = holdable.GetComponent <KMGameCommands>(); if (gameCommands == null) { return; } gameCommands.OnCauseStrike += OnStrike; }
public static IEnumerator Throw(FloatingHoldable holdable, [Group(1)] int?optionalStrength = 3) { int strength = optionalStrength ?? 3; holdable.Pause(); Rigidbody rigidbody = holdable.GetComponent <Rigidbody>(); rigidbody.isKinematic = false; rigidbody.useGravity = true; rigidbody.velocity = Random.onUnitSphere * rigidbody.mass * strength; rigidbody.angularVelocity = Random.onUnitSphere * rigidbody.mass * strength; rigidbody.maxAngularVelocity = 100f; yield return(new WaitForSeconds(2)); rigidbody.isKinematic = true; rigidbody.useGravity = false; holdable.Resume(); }
public static IEnumerator ChangeModuleCount(FloatingHoldable holdable, [Group(1)] int moduleCount) { var device = holdable.GetComponent <FreeplayDevice>(); int currentModuleCount = device.CurrentSettings.ModuleCount; var button = (moduleCount > currentModuleCount ? device.ModuleCountIncrement : device.ModuleCountDecrement).GetComponent <Selectable>(); for (int hitCount = 0; hitCount < Mathf.Abs(moduleCount - currentModuleCount); ++hitCount) { int lastModuleCount = device.CurrentSettings.ModuleCount; button.Trigger(); yield return(new WaitForSeconds(0.01f)); if (lastModuleCount == device.CurrentSettings.ModuleCount) { yield break; } } }
public IEnumerator LetGoBomb() { if (FloatingHoldable.GetComponent <FloatingHoldable>().HoldState != global::FloatingHoldable.HoldStateEnum.Held) { yield break; } IEnumerator turnCoroutine = Hold(true); while (turnCoroutine.MoveNext()) { yield return(turnCoroutine.Current); } while (FloatingHoldable.GetComponent <FloatingHoldable>().HoldState == global::FloatingHoldable.HoldStateEnum.Held) { DeselectObject(Selectable.GetComponent <Selectable>()); yield return(new WaitForSeconds(0.1f)); } }
private static IEnumerator SetBombTimer(FloatingHoldable holdable, int hours, int minutes, int seconds) { if (seconds >= 60) { yield break; } var device = holdable.GetComponent <FreeplayDevice>(); int timeIndex = (hours * 120) + (minutes * 2) + (seconds / 30); DebugHelper.Log("Freeplay time doubling section"); //Double the available free play time. (The doubling stacks with the Multiple bombs module installed) float originalMaxTime = FreeplayDevice.MAX_SECONDS_TO_SOLVE; int maxModules = (int)_maxModuleField.GetValue(device); int multiplier = MultipleBombs.Installed() ? (MultipleBombs.GetMaximumBombCount() * 2) - 1 : 1; float newMaxTime = 600f + (maxModules - 1) * multiplier * 60; FreeplayDevice.MAX_SECONDS_TO_SOLVE = newMaxTime; DebugHelper.Log("Freeplay settings reading section"); float currentTime = device.CurrentSettings.Time; int currentTimeIndex = Mathf.FloorToInt(currentTime) / 30; KeypadButton button = timeIndex > currentTimeIndex ? device.TimeIncrement : device.TimeDecrement; Selectable buttonSelectable = button.GetComponent <Selectable>(); DebugHelper.Log("Freeplay time setting section"); for (int hitCount = 0; hitCount < Mathf.Abs(timeIndex - currentTimeIndex); ++hitCount) { currentTime = device.CurrentSettings.Time; buttonSelectable.Trigger(); yield return(new WaitForSeconds(0.01f)); if (Mathf.FloorToInt(currentTime) == Mathf.FloorToInt(device.CurrentSettings.Time)) { break; } } //Restore original max time, just in case Multiple bombs module was NOT installed, to avoid false detection. FreeplayDevice.MAX_SECONDS_TO_SOLVE = originalMaxTime; }
public static HoldableHandler CreateHandler(KMHoldableCommander commander, FloatingHoldable holdable) { if (commander != null) { commander.ID = holdable.name.ToLowerInvariant().Replace("(clone)", ""); } foreach (Type type in ModHoldableTypes) { if (type?.FullName == null) { continue; } if (holdable.GetComponent(type) == null || !ModHoldableCreators.ContainsKey(type.FullName)) { continue; } return(ModHoldableCreators[type.FullName](commander, holdable)); } return(CreateModComponentSolver(commander, holdable)); }
public IEnumerator Defocus(Selectable selectable, bool frontFace) { IEnumerator gameRoomDefocus = GameRoom.Instance?.BombCommanderDefocus(Bomb, selectable, frontFace); bool continueInvocation = true; if (gameRoomDefocus != null && gameRoomDefocus.MoveNext() && gameRoomDefocus.Current is bool continueInvoke) { continueInvocation = continueInvoke; do { yield return(gameRoomDefocus.Current); } while (gameRoomDefocus.MoveNext()); } if (!continueInvocation || FloatingHoldable == null) { yield break; } FloatingHoldable.Defocus(false, false); selectable.HandleCancel(); selectable.HandleDeselect(); }
public static IEnumerator ChangeBombCount(FloatingHoldable holdable, [Group(1)] int bombCount) { if (!MultipleBombs.Installed()) { yield break; } int currentBombCount = MultipleBombs.GetFreePlayBombCount(); var buttonSelectable = holdable.GetComponent <Selectable>().Children[bombCount > currentBombCount ? 3 : 2]; for (int hitCount = 0; hitCount < Mathf.Abs(bombCount - currentBombCount); ++hitCount) { int lastBombCount = MultipleBombs.GetFreePlayBombCount(); buttonSelectable.Trigger(); yield return(new WaitForSeconds(0.01f)); // Stop here if we hit a maximum or minimum if (lastBombCount == MultipleBombs.GetFreePlayBombCount()) { yield break; } } }
public static void Start(FloatingHoldable holdable) => holdable.GetComponent <FreeplayDevice>().StartButton.GetComponent <Selectable>().Trigger();
private static bool FindCancelBool(FloatingHoldable holdable, Type holdableType, out FieldInfo CancelField) { CancelField = holdableType.GetField("TwitchShouldCancelCommand", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); return(CancelField?.GetValue(holdable.GetComponent(holdableType)) is bool); }
public static IEnumerator ChangeTimer(FloatingHoldable holdable, [Group(1)] int minutes, [Group(2)] int seconds) => SetBombTimer(holdable, 0, minutes, seconds);
public static void ModsOnly(FloatingHoldable holdable, [Group(1)] bool on, string user, bool isWhisper) => SetSetting(holdable, on, user, isWhisper, s => s.OnlyMods, s => s.ModsOnly, TwitchPlaySettings.data.EnableFreeplayModsOnly, TwitchPlaySettings.data.FreePlayModsOnlyDisabled);
public static void Hardcore(FloatingHoldable holdable, [Group(1)] bool on, string user, bool isWhisper) => SetSetting(holdable, on, user, isWhisper, s => s.IsHardCore, s => s.HardcoreToggle, TwitchPlaySettings.data.EnableFreeplayHardcore, TwitchPlaySettings.data.FreePlayHardcoreDisabled);
public static void Needy(FloatingHoldable holdable, [Group(1)] bool on, string user, bool isWhisper) => SetSetting(holdable, on, user, isWhisper, s => s.HasNeedy, s => s.NeedyToggle, TwitchPlaySettings.data.EnableFreeplayNeedy, TwitchPlaySettings.data.FreePlayNeedyDisabled);
public UnsupportedHoldableHandler(KMHoldableCommander commander, FloatingHoldable holdable) : base(commander, holdable) { HelpMessage = "!{0} is not supported by Twitch Plays yet."; }
/// <summary>Adds a holdable, ensuring that the ID in <see cref="Holdables"/> is the same as the one in it's <see cref="TwitchHoldable"/>.</summary> private void AddHoldable(string id, FloatingHoldable holdable, Type commandType = null, bool allowModded = false) { Holdables[id] = new TwitchHoldable(holdable, commandType, allowModded, id); }
public static IEnumerator SelectSearch(FloatingHoldable holdable, [Group(1)] string search) => SelectOnPage(holdable, search: search.Split(new[] { ' ', ',', ';' }, StringSplitOptions.RemoveEmptyEntries));
public static IEnumerator SelectIndex(FloatingHoldable holdable, [Group(1)] int index) => SelectOnPage(holdable, index: index);
public static IEnumerator Select(FloatingHoldable holdable) => SelectOnPage(holdable);
private static IEnumerator SelectOnPage(FloatingHoldable holdable, int index = 0, IList <string> search = null) { if (TwitchPlaysService.Instance.CurrentState != KMGameInfo.State.Setup) { yield break; } if (index > 0 || (search != null && search.Count > 0)) { if ((_currentSelectables == null) || (index > _currentSelectables.Length)) { yield break; } int oldSelectableIndex = _currentSelectableIndex; int i = 0; Selectable newSelectable = null; for (_currentSelectableIndex = 0; _currentSelectableIndex < _currentSelectables.Length; ++_currentSelectableIndex) { if (_currentSelectables[_currentSelectableIndex] == null) { continue; } // Index mode if (index > 0) { if (++i == index) { newSelectable = _currentSelectables[_currentSelectableIndex]; break; } } // Search mode else { var mission = _currentSelectables[_currentSelectableIndex].GetComponent <MissionTableOfContentsMissionEntry>(); if (mission == null) { continue; } string missionName = mission.EntryText.text.ToLowerInvariant(); // Trigger the mission if EITHER // • the first search term matches the mission ID (e.g., "2.1"); OR // • all search terms are found in the mission name if (mission.SubsectionText.text.Equals(search?[0], StringComparison.InvariantCultureIgnoreCase) || search.All(s => missionName.ContainsIgnoreCase(s))) { newSelectable = _currentSelectables[_currentSelectableIndex]; break; } } } if (newSelectable == null) { _currentSelectableIndex = oldSelectableIndex; yield break; } _currentSelectable.HandleDeselect(); _currentSelectable = newSelectable; } if (_currentSelectable == null) { yield break; } _currentSelectable.HandleSelect(true); KTInputManager.Instance.SelectableManager.Select(_currentSelectable, true); // Prevent users from trying to start the tutorial missions, assuming they actually selected a mission. var selectedMission = _currentSelectable.GetComponent <MissionTableOfContentsMissionEntry>(); if (selectedMission != null && MissionManager.Instance.GetMission(selectedMission.MissionID).IsTutorial) { Audio.PlaySound(KMSoundOverride.SoundEffect.Strike, _currentSelectable.transform); IRCConnection.SendMessage("The tutorial missions are currently unsupported and cannot be selected."); yield break; } KTInputManager.Instance.SelectableManager.HandleInteract(); _currentSelectable.OnInteractEnded(); yield return(null); InitializePage(holdable); }
private static IEnumerator SelectOnPage(FloatingHoldable holdable, int index = 0) { if (TwitchPlaysService.Instance.CurrentState != KMGameInfo.State.Gameplay) { yield break; } if (index > 0) { if ((_currentSelectables == null) || (index > _currentSelectables.Length)) { yield break; } int oldSelectableIndex = _currentSelectableIndex; int i = 0; Selectable newSelectable = null; for (_currentSelectableIndex = 0; _currentSelectableIndex < _currentSelectables.Length; ++_currentSelectableIndex) { if (_currentSelectables[_currentSelectableIndex] == null) { continue; } // Index mode if (++i == index) { newSelectable = _currentSelectables[_currentSelectableIndex]; break; } } if (newSelectable == null) { _currentSelectableIndex = oldSelectableIndex; yield break; } _currentSelectable.HandleDeselect(); _currentSelectable = newSelectable; } if (_currentSelectable == null) { yield break; } _currentSelectable.HandleSelect(true); KTInputManager.Instance.SelectableManager.Select(_currentSelectable, true); // Prevent users from trying to select anything that is not the dossier modifier button if (!_currentSelectable.name.StartsWith("SolveDossierModifier")) { Audio.PlaySound(KMSoundOverride.SoundEffect.Strike, _currentSelectable.transform); IRCConnection.SendMessage("This option is not accessable to Twitch Plays users."); yield break; } KTInputManager.Instance.SelectableManager.HandleInteract(); _currentSelectable.OnInteractEnded(); yield return(null); InitializePage(holdable); }
public static IEnumerator Search(FloatingHoldable holdable, [Group(1)] string query) { BombBinder binder = SceneManager.Instance.SetupState.Room.GetComponent <SetupRoom>().BombBinder; var pageManager = binder.MissionTableOfContentsPageManager; var possibleMissions = ModManager.Instance.ModMissions .Concat(MissionManager.Instance.MissionDB.Missions) .Where(mission => Localization.GetLocalizedString(mission.DisplayNameTerm)?.ContainsIgnoreCase(query) == true); switch (possibleMissions.Count()) { case 0: IRCConnection.SendMessage($"There are no missions that match \"{query}\"."); break; case 1: var missionID = possibleMissions.First().ID; binder.ShowMissionDetail(missionID, binder.MissionTableOfContentsPageManager.GetMissionEntry(missionID).Selectable); pageManager.FindMissionInBinder(missionID, out int tocIndex, out int pageIndex, out _); pageManager.SetValue("currentToCIndex", tocIndex); pageManager.SetValue("currentPageIndex", pageIndex); yield return(null); break; default: IRCConnection.SendMessage($"{possibleMissions.Count()} missions match \"{query}\", displaying matching missions in the binder."); var tableOfContentsList = pageManager.GetValue <List <MissionTableOfContents> >("tableOfContentsList"); var previousResults = pageManager.GetToC("toc_tp_search"); if (previousResults != null) { previousResults.ClearPages(); tableOfContentsList.Remove(previousResults); } var missionIDs = possibleMissions.Select(mission => mission.ID); var metaData = new TableOfContentsMetaData { ID = "toc_tp_search", Sections = tableOfContentsList .SelectMany(toc => toc.GetValue <TableOfContentsMetaData>("tocData").Sections) .Where(section => missionIDs.Any(section.MissionIDs.Contains)) .Select(section => new SectionMetaData { SectionNum = section.SectionNum, TitleTerm = section.TitleTerm, MissionIDs = missionIDs.Where(section.MissionIDs.Contains).ToList(), IsUnlocked = section.IsUnlocked }) .ToList() }; MissionTableOfContents missionTableOfContents = new MissionTableOfContents(); missionTableOfContents.Init(pageManager, metaData, binder.Selectable, true); tableOfContentsList.Add(missionTableOfContents); pageManager.ShowToC("toc_tp_search"); yield return(null); break; } InitializePage(holdable); }
private static HoldableHandler CreateModComponentSolver(KMHoldableCommander commander, FloatingHoldable holdable) { DebugLog("Attempting to find a valid process command method to respond with on holdable {0}...", holdable.name); ModHoldableHandlerDelegate modComponentSolverCreator = GenerateModComponentSolverCreator(holdable, out Type holdableType); if (holdableType?.FullName == null || modComponentSolverCreator == null) { return(new UnsupportedHoldableHandler(commander, holdable)); } ModHoldableCreators[holdableType.FullName] = modComponentSolverCreator; return(ModHoldableCreators[holdableType.FullName](commander, holdable)); }