Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TPVoting v1.5.0
TPVoting.dll
Decompiled 3 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using On.RoR2; using R2API.Utils; using RoR2; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TPVoting")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+3ef100031c593a233df6d703419c0c693f98246e")] [assembly: AssemblyProduct("TPVoting")] [assembly: AssemblyTitle("TPVoting")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] namespace TPVoting; internal static class Log { internal static ManualLogSource Logger; internal static void Info(object data) { ManualLogSource logger = Logger; if (logger != null) { logger.LogInfo(data); } } } public static class ChatHelper { private const string GrayColor = "7e91af"; private const string RedColor = "ff0000"; private const string YellowColor = "ffff00"; private const string GreenColor = "32cd32"; private const string SilverColor = "c0c0c0"; private static string ReadyMessage => FirstEntry(PluginConfig.PlayerIsReadyMessages.Value, "r"); private static string CancelMessage => FirstEntry(PluginConfig.PlayerCancelMessages.Value, "f"); private static string FirstEntry(string commaSeparated, string fallback) { string text = commaSeparated.Split(',')[0].Trim(); if (!string.IsNullOrEmpty(text)) { return text; } return fallback; } private static void Send(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage { baseToken = message }); } private static string Exit(string exitName) { return "<color=#ff0000>" + exitName + "</color>"; } private static string Command(string command) { return "<color=#c0c0c0>\"" + command + "\"</color>"; } private static string Count(int numberOfUsersWhoVoted, int numberOfVotingUsers) { return string.Format("<color=#{0}>({1}/{2})</color>", "7e91af", numberOfUsersWhoVoted, numberOfVotingUsers); } public static void VotingStarted(string userName, string exitName, int numberOfUsersWhoVoted, int numberOfVotingUsers) { Send("<color=#32cd32>" + userName + "</color> wants to leave through " + Exit(exitName) + ". Write " + Command(ReadyMessage) + " or press it to ready up, " + Command(CancelMessage) + " to cancel your vote. " + Count(numberOfUsersWhoVoted, numberOfVotingUsers)); } public static void UserIsReady(string userName, int numberOfUsersWhoVoted, int numberOfVotingUsers) { Send("<color=#32cd32>" + userName + "</color> is ready! " + Count(numberOfUsersWhoVoted, numberOfVotingUsers)); } public static void UserIsNotReady(string userName, int numberOfUsersWhoVoted, int numberOfVotingUsers) { Send("<color=#ffff00>" + userName + "</color> is no longer ready. " + Count(numberOfUsersWhoVoted, numberOfVotingUsers)); } public static void ExitUnlocked(string exitName) { Send(Exit(exitName) + " is unlocked! <color=#32cd32>Press it to leave.</color>"); } public static void AllReady(string exitName) { Send("<color=#32cd32>Everyone is ready!</color> Leaving through " + Exit(exitName) + "."); } public static void PlayersNotReady(string exitName) { Send("<color=#ff0000>Players are not ready</color> to leave through " + Exit(exitName) + ". Vote by writing " + Command(ReadyMessage) + " in chat."); } public static void VoteAlreadyRunning(string exitName) { Send("A vote to leave through " + Exit(exitName) + " is already running. Write " + Command(CancelMessage) + " to cancel your vote."); } public static void VotingCancelled() { Send("<color=#ffff00>Nobody is ready, vote ended.</color>"); } public static void TPCountdown(uint unlockTime) { Send(string.Format("Starting exit unlock <color=#{0}>countdown!</color> <color=#{1}>({2} seconds left)</color>", "ffff00", "7e91af", unlockTime)); } public static void TPCountdownCancelled() { Send("<color=#ffff00>Less than half are ready, countdown cancelled.</color>"); } } public static class UsersHelper { public static NetworkUser GetUser(Interactor activator) { CharacterBody body = ((Component)activator).GetComponent<CharacterBody>(); return ((IEnumerable<NetworkUser>)NetworkUser.readOnlyInstancesList).FirstOrDefault((Func<NetworkUser, bool>)((NetworkUser u) => (Object)(object)u.master.GetBody() == (Object)(object)body)); } public static NetworkUser GetUser(CharacterMaster player) { return ((IEnumerable<NetworkUser>)NetworkUser.readOnlyInstancesList).FirstOrDefault((Func<NetworkUser, bool>)((NetworkUser u) => (Object)(object)u.master == (Object)(object)player)); } public static bool IsOneUserOnly() { return NetworkUser.readOnlyInstancesList.Count <= 1; } } public class TPLocker : NetworkBehaviour { public delegate bool ExitLockCheck(GameObject exit); public delegate void LockedExitInteractionAttempt(NetworkUser interactingUser, GameObject exit); private bool bypassLock; public ExitLockCheck IsExitLocked { get; set; } = (GameObject exit) => false; public event LockedExitInteractionAttempt OnLockedExitInteractionAttempt; public void Awake() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown Interactor.PerformInteraction += new hook_PerformInteraction(Interactor_PerformInteraction); SceneExitController.OnEnable += new hook_OnEnable(SceneExitController_OnEnable); SceneExitController.Begin += new hook_Begin(SceneExitController_Begin); } public void OnDestroy() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown Interactor.PerformInteraction -= new hook_PerformInteraction(Interactor_PerformInteraction); SceneExitController.OnEnable -= new hook_OnEnable(SceneExitController_OnEnable); SceneExitController.Begin -= new hook_Begin(SceneExitController_Begin); } public static bool IsStageExit(GameObject interactableObject) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 if (!Object.op_Implicit((Object)(object)interactableObject) || Object.op_Implicit((Object)(object)interactableObject.GetComponent<SeerStationController>())) { return false; } TeleporterInteraction val = default(TeleporterInteraction); if (interactableObject.TryGetComponent<TeleporterInteraction>(ref val)) { return (int)val.activationState == 3; } if (!Object.op_Implicit((Object)(object)interactableObject.GetComponentInParent<SceneExitController>()) && !Object.op_Implicit((Object)(object)interactableObject.GetComponentInChildren<SceneExitController>()) && !Object.op_Implicit((Object)(object)interactableObject.GetComponentInParent<SceneExitControllerColossusPortal>())) { return Object.op_Implicit((Object)(object)interactableObject.GetComponentInChildren<SceneExitControllerColossusPortal>()); } return true; } public static bool IsAvailableFor(GameObject interactableObject, Interactor interactor) { if (Object.op_Implicit((Object)(object)interactableObject) && Object.op_Implicit((Object)(object)interactor)) { return interactableObject.GetComponents<IInteractable>().Any((IInteractable i) => (int)i.GetInteractability(interactor) == 2); } return false; } public static string GetExitName(GameObject exit) { if (!Object.op_Implicit((Object)(object)exit)) { return "exit"; } IDisplayNameProvider component = exit.GetComponent<IDisplayNameProvider>(); string text = ((component != null) ? component.GetDisplayName() : null); if (!string.IsNullOrEmpty(text)) { return text; } return ((Object)exit).name; } public void InteractWithoutLock(Interactor interactor, GameObject exit) { bypassLock = true; try { interactor.AttemptInteraction(exit); } finally { bypassLock = false; } } private void Interactor_PerformInteraction(orig_PerformInteraction orig, Interactor self, GameObject interactableObject) { if (bypassLock || !NetworkServer.active || !IsStageExit(interactableObject) || !IsExitLocked(interactableObject) || !IsAvailableFor(interactableObject, self)) { orig.Invoke(self, interactableObject); return; } NetworkUser user = UsersHelper.GetUser(self); if (!Object.op_Implicit((Object)(object)user)) { orig.Invoke(self, interactableObject); } else { this.OnLockedExitInteractionAttempt?.Invoke(user, interactableObject); } } private void SceneExitController_OnEnable(orig_OnEnable orig, SceneExitController self) { orig.Invoke(self); IEnumerable<IInteractable> source = ((Component)self).GetComponentsInParent<IInteractable>().Concat(((Component)self).GetComponentsInChildren<IInteractable>()).Distinct(); Log.Info("Protected stage exit: " + ((Object)self).name + " (interactables: " + string.Join(", ", source.Select((IInteractable i) => ((object)i).GetType().Name)) + ")"); } private void SceneExitController_Begin(orig_Begin orig, SceneExitController self) { Log.Info("Stage exit begun: " + ((Object)self).name); orig.Invoke(self); } } public enum TPVotingResult { Reset, Cancelled, AllReady, CountdownFinished } public class TPVotingController : NetworkBehaviour { public delegate void TPVotingEnded(TPVotingResult result); private VotingMachine<NetworkUserId> votingMachine = new VotingMachine<NetworkUserId>(); private IEnumerator majorityTPVotingTimer; private bool isMajorityTPVotingTimerRunning; private TPVotingResult votingResult; public event TPVotingEnded OnTPVotingEnded; public bool IsVotingStarted() { return votingMachine.IsVotingStarted; } public bool HasUserVoted(NetworkUser user) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)user)) { return votingMachine.CheckIfVoted(user.id); } return false; } public IEnumerable<NetworkUser> GetReadyUsers() { return NetworkUser.readOnlyInstancesList.Where(HasUserVoted); } public bool Vote(NetworkUser user) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)user) && votingMachine.IsVotingStarted) { return votingMachine.Vote(user.id); } return false; } public void StartVoting(NetworkUser starter, string targetName) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (votingMachine.IsVotingStarted) { return; } List<NetworkUserId> initialVoters = new List<NetworkUserId> { starter.id }; if (PluginConfig.UserAutoVoteOnDeath.Value) { initialVoters.AddRange(from u in NetworkUser.readOnlyInstancesList where Object.op_Implicit((Object)(object)u.master) && u.master.IsDeadAndOutOfLivesServer() select u.id); } ReadOnlyCollection<NetworkUser> readOnlyInstancesList = NetworkUser.readOnlyInstancesList; ChatHelper.VotingStarted(starter.userName, targetName, readOnlyInstancesList.Count((NetworkUser u) => initialVoters.Contains(u.id)), readOnlyInstancesList.Count); votingMachine.StartVoting(readOnlyInstancesList.Select((NetworkUser u) => u.id).ToList(), initialVoters); } public void EndVoting() { if (votingMachine.IsVotingStarted) { votingResult = TPVotingResult.Reset; votingMachine.EndVoting(); } } public void Awake() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown votingMachine.CheckVotingEndCondition = delegate(VotingMachine<NetworkUserId> votingMachine) { if (votingMachine.CountVotes() == 0) { votingResult = TPVotingResult.Cancelled; return true; } if (votingMachine.CheckIfAllVoted()) { votingResult = TPVotingResult.AllReady; return true; } uint unlockTime; if (votingMachine.CheckIfHalfOrMoreVoted()) { if (!isMajorityTPVotingTimerRunning) { unlockTime = PluginConfig.MajorityVotesCountdownTime.Value; ChatHelper.TPCountdown(unlockTime); ((MonoBehaviour)this).StartCoroutine(majorityTPVotingTimer = WaitAndEndVoting()); } } else if (isMajorityTPVotingTimerRunning) { StopMajorityTPVotingTimer(); ChatHelper.TPCountdownCancelled(); } return false; IEnumerator WaitAndEndVoting() { isMajorityTPVotingTimerRunning = true; yield return (object)new WaitForSeconds((float)unlockTime); isMajorityTPVotingTimerRunning = false; votingResult = TPVotingResult.CountdownFinished; votingMachine.EndVoting(); } }; votingMachine.OnVotingEnded += VotingMachine_OnVotingEnded; votingMachine.OnVoterVoted += VotingMachine_OnVoterVoted; votingMachine.OnVoterUnvoted += VotingMachine_OnVoterUnvoted; CharacterMaster.OnBodyDeath += new hook_OnBodyDeath(CharacterMaster_OnBodyDeath); NetworkUser.OnDestroy += new hook_OnDestroy(NetworkUser_OnDestroy); Chat.SendBroadcastChat_ChatMessageBase += new hook_SendBroadcastChat_ChatMessageBase(Chat_SendBroadcastChat_ChatMessageBase); } public void OnDestroy() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown votingMachine.OnVotingEnded -= VotingMachine_OnVotingEnded; votingMachine.OnVoterVoted -= VotingMachine_OnVoterVoted; votingMachine.OnVoterUnvoted -= VotingMachine_OnVoterUnvoted; CharacterMaster.OnBodyDeath -= new hook_OnBodyDeath(CharacterMaster_OnBodyDeath); NetworkUser.OnDestroy -= new hook_OnDestroy(NetworkUser_OnDestroy); Chat.SendBroadcastChat_ChatMessageBase -= new hook_SendBroadcastChat_ChatMessageBase(Chat_SendBroadcastChat_ChatMessageBase); } private void StopMajorityTPVotingTimer() { if (majorityTPVotingTimer != null) { ((MonoBehaviour)this).StopCoroutine(majorityTPVotingTimer); majorityTPVotingTimer = null; } isMajorityTPVotingTimerRunning = false; } private void VotingMachine_OnVotingEnded() { StopMajorityTPVotingTimer(); this.OnTPVotingEnded?.Invoke(votingResult); } private void VotingMachine_OnVoterVoted(NetworkUserId voterID) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) NetworkUser val = ((IEnumerable<NetworkUser>)NetworkUser.readOnlyInstancesList).FirstOrDefault((Func<NetworkUser, bool>)delegate(NetworkUser l) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) NetworkUserId id = l.id; return ((NetworkUserId)(ref id)).Equals(voterID); }); if (Object.op_Implicit((Object)(object)val)) { IReadOnlyDictionary<NetworkUserId, bool> votes = votingMachine.Votes; ChatHelper.UserIsReady(val.userName, votes.Count((KeyValuePair<NetworkUserId, bool> ks) => ks.Value), votes.Count); } } private void VotingMachine_OnVoterUnvoted(NetworkUserId voterID) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) NetworkUser val = ((IEnumerable<NetworkUser>)NetworkUser.readOnlyInstancesList).FirstOrDefault((Func<NetworkUser, bool>)delegate(NetworkUser l) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) NetworkUserId id = l.id; return ((NetworkUserId)(ref id)).Equals(voterID); }); if (Object.op_Implicit((Object)(object)val)) { IReadOnlyDictionary<NetworkUserId, bool> votes = votingMachine.Votes; ChatHelper.UserIsNotReady(val.userName, votes.Count((KeyValuePair<NetworkUserId, bool> ks) => ks.Value), votes.Count); } } private void CharacterMaster_OnBodyDeath(orig_OnBodyDeath orig, CharacterMaster self, CharacterBody body) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(self, body); if (votingMachine.IsVotingStarted && PluginConfig.UserAutoVoteOnDeath.Value && self.IsDeadAndOutOfLivesServer()) { NetworkUser user = UsersHelper.GetUser(self); if (Object.op_Implicit((Object)(object)user)) { votingMachine.Vote(user.id); } } } private void NetworkUser_OnDestroy(orig_OnDestroy orig, NetworkUser self) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(self); if (votingMachine.IsVotingStarted) { votingMachine.RemoveVoter(self.id); } } private void Chat_SendBroadcastChat_ChatMessageBase(orig_SendBroadcastChat_ChatMessageBase orig, ChatMessageBase message) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (!votingMachine.IsVotingStarted) { orig.Invoke(message); return; } UserChatMessage val = (UserChatMessage)(object)((message is UserChatMessage) ? message : null); if (val != null) { NetworkUser component = val.sender.GetComponent<NetworkUser>(); if (Object.op_Implicit((Object)(object)component)) { string message2 = val.text.ToLower().Trim(); if (CheckIfReadyMessage(message2)) { if (votingMachine.Vote(component.id)) { return; } } else if (CheckIfCancelMessage(message2) && votingMachine.Unvote(component.id)) { return; } } } orig.Invoke(message); } private bool CheckIfReadyMessage(string message) { return PluginConfig.PlayerIsReadyMessages.Value.Split(',').Contains(message); } private bool CheckIfCancelMessage(string message) { return PluginConfig.PlayerCancelMessages.Value.Split(',').Contains(message); } } public class TPVotingSystem : NetworkBehaviour { private TPLocker tpLocker; private GameObject voteTarget; private NetworkUserId voteStarterId; private readonly HashSet<GameObject> unlockedExits = new HashSet<GameObject>(); public TPVotingController TPVotingController { get; private set; } public void Awake() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown TPVotingController = ((Component)this).gameObject.AddComponent<TPVotingController>(); TPVotingController.OnTPVotingEnded += TPVotingController_OnTPVotingEnded; tpLocker = ((Component)this).gameObject.AddComponent<TPLocker>(); tpLocker.IsExitLocked = IsExitLocked; tpLocker.OnLockedExitInteractionAttempt += TpLocker_OnLockedExitInteractionAttempt; Run.OnServerSceneChanged += new hook_OnServerSceneChanged(Run_OnServerSceneChanged); } public void OnDestroy() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown TPVotingController.OnTPVotingEnded -= TPVotingController_OnTPVotingEnded; Object.Destroy((Object)(object)TPVotingController); tpLocker.OnLockedExitInteractionAttempt -= TpLocker_OnLockedExitInteractionAttempt; Object.Destroy((Object)(object)tpLocker); Run.OnServerSceneChanged -= new hook_OnServerSceneChanged(Run_OnServerSceneChanged); } private bool IsExitLocked(GameObject exit) { if (!unlockedExits.Contains(exit) && !UsersHelper.IsOneUserOnly()) { return CheckIfCurrentStageQualifyForTPVoting(); } return false; } private void TpLocker_OnLockedExitInteractionAttempt(NetworkUser interactingUser, GameObject exit) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (!TPVotingController.IsVotingStarted()) { voteTarget = exit; voteStarterId = interactingUser.id; TPVotingController.StartVoting(interactingUser, TPLocker.GetExitName(exit)); } else if ((Object)(object)exit == (Object)(object)voteTarget) { if (!TPVotingController.Vote(interactingUser)) { ChatHelper.PlayersNotReady(TPLocker.GetExitName(voteTarget)); } } else { ChatHelper.VoteAlreadyRunning(TPLocker.GetExitName(voteTarget)); } } private void TPVotingController_OnTPVotingEnded(TPVotingResult result) { GameObject exit = voteTarget; voteTarget = null; switch (result) { case TPVotingResult.Cancelled: ChatHelper.VotingCancelled(); break; case TPVotingResult.CountdownFinished: UnlockExit(exit); break; case TPVotingResult.AllReady: LeaveThroughExit(exit); break; } } private void UnlockExit(GameObject exit) { if (Object.op_Implicit((Object)(object)exit)) { unlockedExits.Add(exit); ChatHelper.ExitUnlocked(TPLocker.GetExitName(exit)); } } private void LeaveThroughExit(GameObject exit) { if (!Object.op_Implicit((Object)(object)exit)) { return; } NetworkUser starter = ((IEnumerable<NetworkUser>)NetworkUser.readOnlyInstancesList).FirstOrDefault((Func<NetworkUser, bool>)delegate(NetworkUser u) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) NetworkUserId id = u.id; return ((NetworkUserId)(ref id)).Equals(voteStarterId); }); IEnumerable<NetworkUser> enumerable = ((IEnumerable<NetworkUser>)(object)new NetworkUser[1] { starter }).Concat(from u in TPVotingController.GetReadyUsers() where (Object)(object)u != (Object)(object)starter select u); unlockedExits.Add(exit); foreach (NetworkUser item in enumerable) { Interactor livingInteractor = GetLivingInteractor(item); if (Object.op_Implicit((Object)(object)livingInteractor) && TPLocker.IsAvailableFor(exit, livingInteractor)) { ChatHelper.AllReady(TPLocker.GetExitName(exit)); tpLocker.InteractWithoutLock(livingInteractor, exit); return; } } ChatHelper.ExitUnlocked(TPLocker.GetExitName(exit)); } private static Interactor GetLivingInteractor(NetworkUser user) { if (!Object.op_Implicit((Object)(object)user) || !Object.op_Implicit((Object)(object)user.master)) { return null; } CharacterBody body = user.master.GetBody(); if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.healthComponent) || !body.healthComponent.alive) { return null; } return ((Component)body).GetComponent<Interactor>(); } private void Run_OnServerSceneChanged(orig_OnServerSceneChanged orig, Run self, string sceneName) { orig.Invoke(self, sceneName); TPVotingController.EndVoting(); voteTarget = null; unlockedExits.Clear(); } private bool CheckIfCurrentStageQualifyForTPVoting() { SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene(); if (Object.op_Implicit((Object)(object)sceneDefForCurrentScene)) { return !PluginGlobals.IgnoredStages.Contains(sceneDefForCurrentScene.baseSceneName); } return false; } } public class VotingMachine<VoterID> { public delegate bool VotingEndCondition(VotingMachine<VoterID> votingMachine); public delegate void VotingUpdated(); public delegate void VotingStarted(); public delegate void VotingEnded(); public delegate void VoterVoted(VoterID voterID); public delegate void VoterUnvoted(VoterID voterID); public delegate void VoterRemoved(VoterID voterID); private Dictionary<VoterID, bool> votes = new Dictionary<VoterID, bool>(); public IReadOnlyDictionary<VoterID, bool> Votes => new ReadOnlyDictionary<VoterID, bool>(votes); public bool IsVotingStarted { get; private set; } public VotingEndCondition CheckVotingEndCondition { get; set; } = (VotingMachine<VoterID> votingMachine) => votingMachine.CheckIfAllVoted(); public event VotingUpdated OnVotingUpdated; public event VotingStarted OnVotingStarted; public event VotingEnded OnVotingEnded; public event VoterVoted OnVoterVoted; public event VoterUnvoted OnVoterUnvoted; public event VoterRemoved OnVoterRemoved; public void StartVoting(IReadOnlyCollection<VoterID> voterIDs, IEnumerable<VoterID> initialVoterIDs = null) { votes.Clear(); foreach (VoterID voterID in voterIDs) { votes[voterID] = false; } if (initialVoterIDs != null) { foreach (VoterID item in initialVoterIDs.Where(votes.ContainsKey)) { votes[item] = true; } } IsVotingStarted = true; this.OnVotingStarted?.Invoke(); PostVotingUpdate(); } public void EndVoting() { if (!IsVotingStarted) { Debug.LogWarning((object)"VotingMachine::EndVoting: Trying to end voting while there is no voting started"); return; } votes.Clear(); IsVotingStarted = false; this.OnVotingEnded?.Invoke(); PostVotingUpdate(); } public bool Vote(VoterID voterID) { if (!IsVotingStarted) { Debug.LogWarning((object)"VotingMachine::Vote: Trying to set vote while voting is not started"); return false; } if (!votes.TryGetValue(voterID, out var value)) { Debug.LogWarning((object)$"VotingMachine::Vote: Failed to find voter ID {voterID} in voter list"); return false; } if (value) { return false; } votes[voterID] = true; this.OnVoterVoted?.Invoke(voterID); PostVotingUpdate(); return true; } public bool Unvote(VoterID voterID) { if (!IsVotingStarted) { return false; } if (votes.TryGetValue(voterID, out var value) && value) { votes[voterID] = false; this.OnVoterUnvoted?.Invoke(voterID); PostVotingUpdate(); return true; } return false; } public bool RemoveVoter(VoterID voterID) { if (!IsVotingStarted) { return false; } if (votes.ContainsKey(voterID)) { votes.Remove(voterID); this.OnVoterRemoved?.Invoke(voterID); PostVotingUpdate(); return true; } return false; } private void PostVotingUpdate() { this.OnVotingUpdated?.Invoke(); if (IsVotingStarted && CheckVotingEndCondition(this)) { EndVoting(); } } public bool CheckIfVoted(VoterID voterID) { bool value; return votes.TryGetValue(voterID, out value) && value; } public int CountVotes() { return votes.Count((KeyValuePair<VoterID, bool> kv) => kv.Value); } public bool CheckIfAllVoted() { return votes.All((KeyValuePair<VoterID, bool> kv) => kv.Value); } public bool CheckIfThereIsOnlyOneVoter() { return votes.Count == 1; } public bool CheckIfHalfOrMoreVoted() { return CountVotes() >= Mathf.CeilToInt((float)votes.Count / 2f); } } internal class PluginConfig { public static ConfigEntry<string> PlayerIsReadyMessages; public static ConfigEntry<string> PlayerCancelMessages; public static ConfigEntry<string> IgnoredGameModes; public static ConfigEntry<uint> MajorityVotesCountdownTime; public static ConfigEntry<bool> UserAutoVoteOnDeath; } public static class PluginGlobals { public static List<string> IgnoredStages = new List<string> { "arena", "moon", "moon2", "limbo", "outro", "voidraid" }; } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInIncompatibility("com.Mordrog.TPVoting")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInPlugin("kaitorque.TPVoting", "TPVoting", "1.5.0")] public class TPVotingPlugin : BaseUnityPlugin { public const string ModVer = "1.5.0"; public const string ModName = "TPVoting"; public const string ModGuid = "kaitorque.TPVoting"; public const string OriginalModGuid = "com.Mordrog.TPVoting"; public TPVotingSystem TPVotingSystem { get; private set; } public TPVotingPlugin() { Log.Logger = ((BaseUnityPlugin)this).Logger; InitConfig(); } public void Awake() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Run.Awake += new hook_Awake(Run_Awake); Run.OnDestroy += new hook_OnDestroy(Run_OnDestroy); } private void Run_Awake(orig_Awake orig, Run self) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(self); if (!PluginConfig.IgnoredGameModes.Value.Contains(GameModeCatalog.GetGameModeName(self.gameModeIndex))) { TPVotingSystem = ((Component)this).gameObject.AddComponent<TPVotingSystem>(); } } private void Run_OnDestroy(orig_OnDestroy orig, Run self) { orig.Invoke(self); if (Object.op_Implicit((Object)(object)TPVotingSystem)) { Object.Destroy((Object)(object)TPVotingSystem); } } private void InitConfig() { PluginConfig.PlayerIsReadyMessages = ((BaseUnityPlugin)this).Config.Bind<string>("Settings", "PlayerIsReadyMessages", "r,rdy,ready", "The message the player has to write in the chat to confirm they are ready. Values must be separated by comma."); PluginConfig.PlayerCancelMessages = ((BaseUnityPlugin)this).Config.Bind<string>("Settings", "PlayerCancelMessages", "f", "The message the player has to write in the chat to remove their vote. Values must be separated by comma."); PluginConfig.IgnoredGameModes = ((BaseUnityPlugin)this).Config.Bind<string>("Settings", "IgnoredGameModes", "InfiniteTowerRun", "Gamemode in which tp voting should not work."); PluginConfig.MajorityVotesCountdownTime = ((BaseUnityPlugin)this).Config.Bind<uint>("Settings", "MajorityVotesCountdownTime", 30u, "Countdown in seconds to unlock the voted exit when half or most of the players are ready."); PluginConfig.UserAutoVoteOnDeath = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "UserAutoVoteOnDeath", true, "Should players auto vote tp when they die."); } }