Decompiled source of TPVoting v1.5.0

TPVoting.dll

Decompiled 3 hours ago
using 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.");
	}
}