Decompiled source of PeakImposter v1.0.4

BepInEx/plugins/PeakImposter/PeakImposter.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Peak;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("Zorro.Core.Runtime")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PeakImposter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("1.0.4")]
[assembly: AssemblyProduct("PeakImposter")]
[assembly: AssemblyTitle("PeakImposter")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.4.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PeakImposter
{
	public static class DaggerUtil
	{
		public const string FallbackPrefabName = "RitualDagger";

		private static bool _resolved;

		private static string _prefabName;

		private static ushort _itemId;

		public static string PrefabName
		{
			get
			{
				Resolve();
				return _prefabName;
			}
		}

		public static ushort ItemId
		{
			get
			{
				Resolve();
				return _itemId;
			}
		}

		public static bool Resolved
		{
			get
			{
				Resolve();
				return _prefabName != null;
			}
		}

		public static void Invalidate()
		{
			_resolved = false;
			_prefabName = null;
			_itemId = 0;
		}

		private static void Resolve()
		{
			if (_resolved)
			{
				return;
			}
			ItemDatabase instance;
			try
			{
				instance = SingletonAsset<ItemDatabase>.Instance;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ItemDatabase not available yet: " + ex.Message));
				return;
			}
			if ((Object)(object)instance == (Object)null || instance.itemLookup == null || instance.itemLookup.Count == 0)
			{
				return;
			}
			_resolved = true;
			foreach (KeyValuePair<ushort, Item> item in instance.itemLookup)
			{
				Item value = item.Value;
				if (!((Object)(object)value == (Object)null) && !((Object)(object)((Component)value).GetComponentInChildren<RitualDaggerFeedBehavior>(true) == (Object)null))
				{
					_prefabName = ((Object)((Component)value).gameObject).name;
					_itemId = item.Key;
					Plugin.Log.LogInfo((object)$"Ritual dagger found: '{_prefabName}' (itemID {_itemId})");
					return;
				}
			}
			Item val = default(Item);
			if (ItemDatabase.TryGetItem("RitualDagger", ref val) && (Object)(object)val != (Object)null)
			{
				_prefabName = ((Object)((Component)val).gameObject).name;
				_itemId = val.itemID;
				Plugin.Log.LogWarning((object)("Ritual dagger only found by fallback name: '" + _prefabName + "'"));
			}
			else
			{
				Plugin.Log.LogError((object)"Ritual dagger not found in the ItemDatabase!");
			}
		}

		public static bool IsDagger(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			if (Resolved && item.itemID == _itemId)
			{
				return true;
			}
			return (Object)(object)((Component)item).GetComponentInChildren<RitualDaggerFeedBehavior>(true) != (Object)null;
		}

		public static bool IsDagger(ushort itemId)
		{
			if (Resolved)
			{
				return itemId == _itemId;
			}
			return false;
		}

		public static bool PlayerHasDagger(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (player.itemSlots != null)
			{
				ItemSlot[] itemSlots = player.itemSlots;
				foreach (ItemSlot val in itemSlots)
				{
					if (val != null && !val.IsEmpty() && IsDagger(val.prefab))
					{
						return true;
					}
				}
			}
			if (player.tempFullSlot != null && !player.tempFullSlot.IsEmpty() && IsDagger(player.tempFullSlot.prefab))
			{
				return true;
			}
			Character character = player.character;
			if ((Object)(object)character != (Object)null && (Object)(object)character.data != (Object)null && (Object)(object)character.data.currentItem != (Object)null && IsDagger(character.data.currentItem))
			{
				return true;
			}
			return false;
		}
	}
	public class ImposterManager : MonoBehaviour, IOnEventCallback
	{
		private readonly Dictionary<int, int> _killsByActor = new Dictionary<int, int>();

		private Coroutine _assignRoutine;

		private Coroutine _grantRoutine;

		private bool _rolesAssignedThisRun;

		private bool _callbacksRegistered;

		private float _lastCampfireHandled = -999f;

		private bool _rerollRunning;

		private Coroutine _rerollRoutine;

		private int _sheriffActor = -1;

		private const int RerollMinAlive = 2;

		private int _sheriffShotsLeft;

		private int _roleSceneHandle = -1;

		private readonly HashSet<int> _impostersThisRun = new HashSet<int>();

		private readonly HashSet<int> _sheriffsThisRun = new HashSet<int>();

		private readonly Dictionary<int, int> _correctCallsByActor = new Dictionary<int, int>();

		public static ImposterManager Instance { get; private set; }

		public string ToastText { get; private set; } = "";

		public float ToastUntil { get; private set; }

		public void Toast(string text, float seconds = 4f)
		{
			ToastText = text;
			ToastUntil = Time.time + seconds;
		}

		private void Awake()
		{
			Instance = this;
		}

		private void Update()
		{
			if (!_callbacksRegistered)
			{
				try
				{
					if (PhotonNetwork.NetworkingClient == null)
					{
						return;
					}
					PhotonNetwork.AddCallbackTarget((object)this);
					_callbacksRegistered = true;
					Plugin.Log.LogInfo((object)"Photon callbacks registered.");
				}
				catch (Exception ex)
				{
					Plugin.Log.LogDebug((object)("Photon not ready yet: " + ex.Message));
				}
			}
			if (Plugin.Cfg.DebugMode.Value)
			{
				HandleDebugKeys();
			}
		}

		private void OnDestroy()
		{
			if (_callbacksRegistered)
			{
				try
				{
					PhotonNetwork.RemoveCallbackTarget((object)this);
				}
				catch
				{
				}
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		public void OnRunStarted()
		{
			//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_0008: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			int num = SceneHandle.op_Implicit(((Scene)(ref activeScene)).handle);
			if (!PhotonNetwork.IsMasterClient && ImposterState.Active && _roleSceneHandle == num)
			{
				Plugin.Log.LogInfo((object)"Role arrived before this client's own run start - keeping it.");
			}
			else
			{
				ImposterState.Reset();
			}
			_killsByActor.Clear();
			_impostersThisRun.Clear();
			_sheriffsThisRun.Clear();
			_correctCallsByActor.Clear();
			_rolesAssignedThisRun = false;
			_lastCampfireHandled = -999f;
			StopReroll();
			DaggerUtil.Invalidate();
			SheriffBook.Invalidate();
			if (!Plugin.Cfg.Enabled.Value)
			{
				Plugin.Log.LogInfo((object)"Imposter mode is disabled in the config.");
				return;
			}
			if (_assignRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_assignRoutine);
			}
			if (PhotonNetwork.IsMasterClient)
			{
				_assignRoutine = ((MonoBehaviour)this).StartCoroutine(InitialAssignRoutine());
			}
			else
			{
				Plugin.Log.LogInfo((object)"Run started - waiting for the host to assign roles.");
			}
		}

		public void OnReturnedToAirport()
		{
			ImposterState.Reset();
			_killsByActor.Clear();
			_impostersThisRun.Clear();
			_sheriffsThisRun.Clear();
			_correctCallsByActor.Clear();
			_rolesAssignedThisRun = false;
			StopReroll();
		}

		private void StopReroll()
		{
			if (_rerollRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_rerollRoutine);
			}
			_rerollRoutine = null;
			_rerollRunning = false;
		}

		private IEnumerator InitialAssignRoutine()
		{
			float deadline = Time.realtimeSinceStartup + Plugin.Cfg.AssignTimeout.Value;
			yield return (object)new WaitForSecondsRealtime(2f);
			while (Time.realtimeSinceStartup < deadline)
			{
				int num = ((PhotonNetwork.CurrentRoom == null) ? 1 : PhotonNetwork.CurrentRoom.PlayerCount);
				if (PlayerHandler.GetAllPlayerCharacters().Count >= num)
				{
					break;
				}
				yield return (object)new WaitForSecondsRealtime(0.5f);
			}
			yield return (object)new WaitForSecondsRealtime(Plugin.Cfg.AssignDelay.Value);
			AssignRoles(isReroll: false);
		}

		private bool AssignRoles(bool isReroll)
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				return false;
			}
			Character val = default(Character);
			List<int> list = (from a in ImposterNet.GetActivePlayerActors()
				where PlayerHandler.TryGetCharacter(a, ref val)
				select a).ToList();
			List<int> list2 = list.Where(IsAlive).ToList();
			int num = (isReroll ? 2 : Plugin.Cfg.MinPlayers.Value);
			if (list2.Count < num)
			{
				Plugin.Log.LogWarning((object)$"Not enough living players ({list2.Count}) for imposter mode.");
				if (!isReroll)
				{
					ImposterNet.SendAbort(Loc.T("toast.minplayers", Plugin.Cfg.MinPlayers.Value, list2.Count));
				}
				return false;
			}
			ImposterState.ProtectionSeconds = Plugin.Cfg.ProtectionSeconds.Value;
			ImposterState.CooldownSeconds = Plugin.Cfg.CooldownSeconds.Value;
			ImposterState.InfiniteDagger = Plugin.Cfg.InfiniteDagger.Value;
			ImposterState.SilentKill = Plugin.Cfg.SilentKill.Value;
			ImposterState.HideFeedIndicator = Plugin.Cfg.HideFeedIndicator.Value;
			ImposterState.OnlyImposterCanKill = Plugin.Cfg.OnlyImposterCanKill.Value;
			float num2 = (isReroll ? Plugin.Cfg.RerollProtectionSeconds.Value : Plugin.Cfg.ProtectionSeconds.Value);
			float protectionEndRunTime = ImposterState.RunTime + num2;
			HashSet<int> previous = new HashSet<int>(ImposterState.ImposterActors);
			List<int> list3 = list2;
			if (isReroll && Plugin.Cfg.NeverSameImposterTwice.Value)
			{
				List<int> list4 = list2.Where((int a) => !previous.Contains(a)).ToList();
				if (list4.Count >= Plugin.Cfg.ImposterCount.Value)
				{
					list3 = list4;
				}
			}
			int count = Mathf.Clamp(Plugin.Cfg.ImposterCount.Value, 1, Mathf.Max(1, list3.Count - 1));
			int[] array = list3.OrderBy((int _) => Random.value).Take(count).ToArray();
			ImposterState.ImposterActors.Clear();
			int[] array2 = array;
			foreach (int item in array2)
			{
				ImposterState.ImposterActors.Add(item);
				_impostersThisRun.Add(item);
			}
			ImposterState.ProtectionEndRunTime = protectionEndRunTime;
			Plugin.Log.LogInfo((object)((isReroll ? "New imposters: " : "Imposters chosen: ") + string.Join(", ", array.Select(NameOf).ToArray())));
			_sheriffActor = -1;
			if (Plugin.Cfg.EnableSheriff.Value && list2.Count >= Plugin.Cfg.SheriffMinPlayers.Value)
			{
				List<int> list5 = list2.Where((int a) => !ImposterState.ImposterActors.Contains(a)).ToList();
				if (list5.Count > 0)
				{
					_sheriffActor = list5[Random.Range(0, list5.Count)];
					_sheriffsThisRun.Add(_sheriffActor);
					Plugin.Log.LogInfo((object)("Sheriff: " + NameOf(_sheriffActor)));
				}
			}
			_sheriffShotsLeft = Plugin.Cfg.SheriffShots.Value;
			int num4 = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1));
			foreach (int item2 in list)
			{
				bool num5 = ImposterState.ImposterActors.Contains(item2);
				Role role = (num5 ? Role.Imposter : ((item2 != _sheriffActor) ? Role.Crew : Role.Sheriff));
				int[] array3 = (num5 ? array : new int[0]);
				int num6 = ((role == Role.Sheriff) ? _sheriffShotsLeft : 0);
				if (item2 == num4)
				{
					ImposterState.SheriffShotsLeft = num6;
					ImposterState.SheriffAimSeconds = Plugin.Cfg.SheriffAimSeconds.Value;
					ApplyRole(role, array3, isReroll);
				}
				else
				{
					ImposterNet.SendRole(item2, role, array3, protectionEndRunTime, isReroll, num6, Plugin.Cfg.SheriffAimSeconds.Value);
				}
			}
			_rolesAssignedThisRun = true;
			if (Plugin.Cfg.GiveDaggerOnStart.Value)
			{
				if (_grantRoutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_grantRoutine);
				}
				_grantRoutine = ((MonoBehaviour)this).StartCoroutine(GrantDaggersRoutine(array));
			}
			return true;
		}

		private static bool IsAlive(int actor)
		{
			Character val = default(Character);
			if (PlayerHandler.TryGetCharacter(actor, ref val) && (Object)(object)val != (Object)null && (Object)(object)val.data != (Object)null)
			{
				return !val.data.dead;
			}
			return false;
		}

		private static int CountAliveActors()
		{
			return ImposterNet.GetActivePlayerActors().Count(IsAlive);
		}

		private static string NameOf(int actor)
		{
			if (ImposterState.IsDebugFake(actor))
			{
				return ImposterState.DebugFakeName(actor);
			}
			Character val = default(Character);
			if (PlayerHandler.TryGetCharacter(actor, ref val) && (Object)(object)val != (Object)null)
			{
				return val.characterName;
			}
			Player val2 = ((PhotonNetwork.CurrentRoom != null) ? PhotonNetwork.CurrentRoom.GetPlayer(actor, false) : null);
			if (val2 == null)
			{
				return "Scout #" + actor;
			}
			return val2.NickName;
		}

		private static bool HasRealFreeSlot(Player player)
		{
			if ((Object)(object)player == (Object)null || player.itemSlots == null)
			{
				return false;
			}
			for (int i = 0; i < player.itemSlots.Length; i++)
			{
				if (player.itemSlots[i] != null && player.itemSlots[i].IsEmpty())
				{
					return true;
				}
			}
			return false;
		}

		private IEnumerator GrantDaggersRoutine(int[] actors)
		{
			float dbDeadline = Time.time + 20f;
			while (!DaggerUtil.Resolved && Time.time < dbDeadline)
			{
				yield return (object)new WaitForSeconds(0.5f);
			}
			if (!DaggerUtil.Resolved)
			{
				Plugin.Log.LogError((object)"Dagger not found in the ItemDatabase - cannot hand it out.");
				foreach (int actor in actors)
				{
					SendToast(actor, "toast.dagger.failed", 8f);
				}
				yield break;
			}
			yield return (object)new WaitForSeconds(Plugin.Cfg.DaggerSpawnDelay.Value);
			List<int> pending = new List<int>(actors);
			HashSet<int> warned = new HashSet<int>();
			float deadline = Time.time + Plugin.Cfg.DaggerGrantTimeout.Value;
			Player val = default(Player);
			ItemSlot val2 = default(ItemSlot);
			while (pending.Count > 0 && Time.time < deadline)
			{
				if (Plugin.Cfg.GiveDaggerAfterProtection.Value && ImposterState.ProtectionActive)
				{
					yield return (object)new WaitForSeconds(1f);
					continue;
				}
				for (int num = pending.Count - 1; num >= 0; num--)
				{
					int num2 = pending[num];
					if (!ImposterState.ImposterActors.Contains(num2))
					{
						pending.RemoveAt(num);
					}
					else if (PlayerHandler.TryGetPlayer(num2, ref val) && !((Object)(object)val == (Object)null))
					{
						Character character = val.character;
						if (!((Object)(object)character == (Object)null) && !((Object)(object)character.data == (Object)null) && !character.data.dead)
						{
							if (DaggerUtil.PlayerHasDagger(val))
							{
								Plugin.Log.LogInfo((object)(NameOf(num2) + " already has a dagger - not handing out a second one."));
								SetWaiting(num2, waiting: false);
								pending.RemoveAt(num);
							}
							else if (!HasRealFreeSlot(val))
							{
								if (warned.Add(num2))
								{
									SetWaiting(num2, waiting: true);
									SendToast(num2, "toast.dagger.waiting", 10f);
								}
							}
							else if (val.AddItem(DaggerUtil.ItemId, (ItemInstanceData)null, ref val2))
							{
								try
								{
									character.refs.items.RefreshAllCharacterCarryWeight();
								}
								catch
								{
								}
								SetWaiting(num2, waiting: false);
								SendToast(num2, "toast.dagger.given", 6f);
								Plugin.Log.LogInfo((object)("Dagger handed to " + NameOf(num2) + "."));
								pending.RemoveAt(num);
							}
						}
					}
				}
				yield return (object)new WaitForSeconds(1f);
			}
			foreach (int item in pending)
			{
				Plugin.Log.LogWarning((object)("Handing the dagger to " + NameOf(item) + " timed out."));
				SendToast(item, "toast.dagger.failed", 8f);
				SetWaiting(item, waiting: false);
			}
		}

		public void HandleBookRequest(int sheriffActor)
		{
			if (!PhotonNetwork.IsMasterClient || sheriffActor != _sheriffActor || _sheriffShotsLeft <= 0)
			{
				return;
			}
			if (!SheriffBook.Resolved)
			{
				Plugin.Log.LogError((object)"No guidebook in the ItemDatabase - cannot hand out the ledger.");
				SendToast(sheriffActor, "toast.book.failed", 8f);
			}
			else
			{
				Player val = default(Player);
				if (!PlayerHandler.TryGetPlayer(sheriffActor, ref val) || (Object)(object)val == (Object)null)
				{
					return;
				}
				Character character = val.character;
				if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null || character.data.dead)
				{
					return;
				}
				ItemSlot val2 = default(ItemSlot);
				if (!HasRealFreeSlot(val))
				{
					SendToast(sheriffActor, "toast.book.waiting", 8f);
				}
				else if (val.AddItem(SheriffBook.ItemId, (ItemInstanceData)null, ref val2) && val2 != null && val2.data != null)
				{
					try
					{
						character.refs.items.RefreshAllCharacterCarryWeight();
					}
					catch
					{
					}
					ManualLogSource log = Plugin.Log;
					string[] obj2 = new string[7]
					{
						"Ledger handed to ",
						NameOf(sheriffActor),
						" (slot ",
						val2.itemSlotID.ToString(),
						", Guid ",
						null,
						null
					};
					Guid guid = val2.data.guid;
					obj2[5] = guid.ToString();
					obj2[6] = ").";
					log.LogInfo((object)string.Concat(obj2));
					GrantBookTo(sheriffActor, val2.itemSlotID, val2.data.guid);
				}
			}
		}

		private void GrantBookTo(int actor, byte slotId, Guid ledgerId)
		{
			if (PhotonNetwork.LocalPlayer != null && actor == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				if ((Object)(object)SheriffAbility.Instance != (Object)null)
				{
					SheriffAbility.Instance.OnBookGranted(slotId, ledgerId);
				}
			}
			else
			{
				ImposterNet.SendBookGranted(actor, slotId, ledgerId);
			}
		}

		private void SendToast(int actor, string locKey, float seconds)
		{
			if (PhotonNetwork.LocalPlayer != null && actor == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				Toast(Loc.T(locKey), seconds);
			}
			else
			{
				ImposterNet.SendToast(actor, locKey, seconds);
			}
		}

		private void SetWaiting(int actor, bool waiting)
		{
			if (PhotonNetwork.LocalPlayer != null && actor == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				ImposterState.WaitingForDaggerSlot = waiting;
			}
			else
			{
				ImposterNet.SendWaiting(actor, waiting);
			}
		}

		private void SetCeasefire(bool on)
		{
			if (on)
			{
				if (_grantRoutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_grantRoutine);
				}
				_grantRoutine = null;
				foreach (int imposterActor in ImposterState.ImposterActors)
				{
					if (!ImposterState.IsDebugFake(imposterActor))
					{
						SetWaiting(imposterActor, waiting: false);
					}
				}
			}
			ApplyCeasefire(on);
			ImposterNet.SendCeasefire(on);
		}

		private void HandleCeasefire(EventData photonEvent)
		{
			if (photonEvent.CustomData is object[] array && array.Length >= 1)
			{
				ApplyCeasefire((bool)array[0]);
			}
		}

		private void ApplyCeasefire(bool on)
		{
			if (ImposterState.Ceasefire != on)
			{
				ImposterState.Ceasefire = on;
				Plugin.Log.LogInfo((object)(on ? "Ceasefire until the re-roll." : "Ceasefire over."));
				if (on && ImposterState.IsImposter)
				{
					ImposterState.WaitingForDaggerSlot = false;
					((MonoBehaviour)this).StartCoroutine(RemoveOwnDagger());
					Toast(Loc.T("toast.ceasefire.imposter"), 6f);
				}
			}
		}

		private IEnumerator RemoveOwnDagger()
		{
			Character c = Character.localCharacter;
			if ((Object)(object)c == (Object)null || (Object)(object)c.player == (Object)null)
			{
				yield break;
			}
			if ((Object)(object)c.data != (Object)null && (Object)(object)c.data.currentItem != (Object)null && DaggerUtil.IsDagger(c.data.currentItem))
			{
				c.refs.items.EquipSlot(Optionable<byte>.None);
				yield return (object)new WaitForSeconds(0.5f);
			}
			ItemSlot[] itemSlots = c.player.itemSlots;
			for (byte b = 0; b < itemSlots.Length; b++)
			{
				ItemSlot val = itemSlots[b];
				if (val != null && !val.IsEmpty() && !((Object)(object)val.prefab == (Object)null) && DaggerUtil.IsDagger(val.prefab))
				{
					c.player.EmptySlot(Optionable<byte>.Some(b), true);
					Plugin.Log.LogInfo((object)("Removed own dagger from slot " + b + "."));
				}
			}
		}

		public void OnCampfireLit(bool advancesBiome, Vector3 campfirePosition)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			if (!advancesBiome || !PhotonNetwork.IsMasterClient || !_rolesAssignedThisRun || _rerollRunning || Time.time - _lastCampfireHandled < 10f)
			{
				return;
			}
			_lastCampfireHandled = Time.time;
			List<Character> list = Character.AllCharacters.Where((Character c) => (Object)(object)c != (Object)null && (Object)(object)c.data != (Object)null && !c.data.dead && (Object)(object)((MonoBehaviourPun)c).photonView != (Object)null && ((MonoBehaviourPun)c).photonView.Owner != null).ToList();
			bool flag = list.Count > 0 && ImposterState.ImposterActors.Count > 0 && list.All((Character c) => ImposterState.ImposterActors.Contains(((MonoBehaviourPun)c).photonView.Owner.ActorNumber));
			SpawnConsolationItems(campfirePosition, !flag);
			if (flag)
			{
				Plugin.Log.LogInfo((object)"Imposter was the sole survivor - reward goes to them.");
				foreach (Character item in list)
				{
					Reward(((MonoBehaviourPun)item).photonView.Owner.ActorNumber);
				}
			}
			else if (Plugin.Cfg.RewardSurvivingScouts.Value)
			{
				Plugin.Log.LogInfo((object)"Scouts made it through the biome - reward goes to them.");
				foreach (Character item2 in list)
				{
					int actorNumber = ((MonoBehaviourPun)item2).photonView.Owner.ActorNumber;
					if (!ImposterState.ImposterActors.Contains(actorNumber))
					{
						Reward(actorNumber);
					}
				}
			}
			if (Plugin.Cfg.RerollAtCampfire.Value)
			{
				if (Plugin.Cfg.CeasefireAtCampfire.Value)
				{
					SetCeasefire(on: true);
				}
				_rerollRoutine = ((MonoBehaviour)this).StartCoroutine(RerollRoutine());
			}
		}

		private unsafe void SpawnConsolationItems(Vector3 center, bool scoutsSurvived)
		{
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Invalid comparison between Unknown and I4
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Cfg.ConsolationItems.Value || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			int num = Mathf.Clamp(((PhotonNetwork.CurrentRoom == null) ? 1 : PhotonNetwork.CurrentRoom.PlayerCount) * Plugin.Cfg.ConsolationItemsPerPlayer.Value, 0, 24);
			if (num <= 0)
			{
				return;
			}
			SpawnPool val = ToSpawnPool(Plugin.Cfg.ConsolationItemPool.Value);
			Rarity val2 = (scoutsSurvived ? Plugin.Cfg.ConsolationMinRarityOnSuccess.Value : Plugin.Cfg.ConsolationMinRarityOnWipe.Value);
			List<GameObject> list;
			try
			{
				list = (((int)val2 > 0) ? PickByRarity(val, val2, num) : null) ?? LootData.GetRandomItems(val, num, true, (GameObject)null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Consolation items: loot table not readable - " + ex.Message));
				return;
			}
			if (list == null)
			{
				return;
			}
			Plugin.Log.LogInfo((object)("Consolation items: " + (scoutsSurvived ? "scouts survived" : "imposter wiped the group") + ", minimum rarity " + ((object)(*(Rarity*)(&val2))/*cast due to .constrained prefix*/).ToString() + "."));
			int num2 = 0;
			foreach (GameObject item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					float num3 = Random.value * MathF.PI * 2f;
					float num4 = Random.Range(1.4f, 3.2f);
					Vector3 val3 = center + new Vector3(Mathf.Cos(num3) * num4, 1.3f, Mathf.Sin(num3) * num4);
					try
					{
						PhotonNetwork.Instantiate("0_Items/" + ((Object)item).name, val3, Quaternion.identity, (byte)0, (object[])null);
						num2++;
					}
					catch (Exception ex2)
					{
						Plugin.Log.LogWarning((object)("Consolation item '" + ((Object)item).name + "' could not be spawned: " + ex2.Message));
					}
				}
			}
			if (num2 > 0)
			{
				Plugin.Log.LogInfo((object)$"Spawned {num2} consolation items at the campfire.");
				ImposterNet.SendToastAll("toast.consolation", 7f);
			}
			if (scoutsSurvived && Plugin.Cfg.BonusItemOnSuccess.Value)
			{
				SpawnBonusItem(center);
			}
		}

		private static SpawnPool ToSpawnPool(LootPool pool)
		{
			return (SpawnPool)(pool switch
			{
				LootPool.AllItems => 134217728, 
				LootPool.RespawnChest => 131072, 
				LootPool.AncientLuggage => 32768, 
				_ => 33554432, 
			});
		}

		private unsafe void SpawnBonusItem(Vector3 center)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Invalid comparison between Unknown and I4
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			SpawnPool pool = ToSpawnPool(Plugin.Cfg.BonusItemPool.Value);
			Rarity val = Plugin.Cfg.BonusItemMinRarity.Value;
			while ((int)val >= 0)
			{
				List<GameObject> list;
				try
				{
					list = PickByRarity(pool, val, 1);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Bonus item: loot table not readable - " + ex.Message));
					return;
				}
				if (list == null || list.Count == 0 || (Object)(object)list[0] == (Object)null)
				{
					if ((int)val == 0)
					{
						break;
					}
					val = (Rarity)(val - 1);
					continue;
				}
				GameObject val2 = list[0];
				Vector3 val3 = center + new Vector3(0f, 1.6f, 0f);
				try
				{
					PhotonNetwork.Instantiate("0_Items/" + ((Object)val2).name, val3, Quaternion.identity, (byte)0, (object[])null);
					Plugin.Log.LogInfo((object)("Spawned bonus item '" + ((Object)val2).name + "' (" + ((object)(*(Rarity*)(&val))/*cast due to .constrained prefix*/).ToString() + ") at the campfire."));
					ImposterNet.SendToastAll("toast.bonus", 7f);
					return;
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogWarning((object)("Bonus item '" + ((Object)val2).name + "' could not be spawned: " + ex2.Message));
					return;
				}
			}
			Plugin.Log.LogWarning((object)("No bonus item found - pool " + ((object)(*(SpawnPool*)(&pool))/*cast due to .constrained prefix*/).ToString() + " is empty."));
		}

		private unsafe static List<GameObject> PickByRarity(SpawnPool pool, Rarity minRarity, int count)
		{
			//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)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			List<Item> list = (from item in LootData.GetAllItemsInPool(pool)
				where (Object)(object)item != (Object)null
				select item).Where(delegate(Item item)
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				LootData component = ((Component)item).GetComponent<LootData>();
				return (Object)(object)component != (Object)null && component.Rarity >= minRarity && component.IsValidToSpawn();
			}).ToList();
			if (list.Count == 0)
			{
				Plugin.Log.LogWarning((object)("No items of rarity " + ((object)(*(Rarity*)(&minRarity))/*cast due to .constrained prefix*/).ToString() + " or higher in the pool - falling back to the default selection."));
				return null;
			}
			List<GameObject> list2 = new List<GameObject>();
			for (int num = 0; num < count; num++)
			{
				list2.Add(((Component)list[Random.Range(0, list.Count)]).gameObject);
			}
			return list2;
		}

		private void Reward(int actor)
		{
			if (PhotonNetwork.LocalPlayer != null && actor == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				ApplyReward();
			}
			else
			{
				ImposterNet.SendReward(actor);
			}
		}

		private IEnumerator RerollRoutine()
		{
			_rerollRunning = true;
			try
			{
				yield return (object)new WaitForSeconds(Plugin.Cfg.RerollDelay.Value);
				bool flag = PhotonNetwork.CurrentRoom == null || PhotonNetwork.CurrentRoom.PlayerCount <= 1;
				if (ImposterState.DebugDummies.Count > 0 && !flag)
				{
					Plugin.Log.LogWarning((object)"DEBUG: other players joined - dummies discarded, doing a real re-roll.");
					ImposterState.DebugDummies.Clear();
				}
				if (ImposterState.DebugDummies.Count > 0)
				{
					ImposterNet.SendRerollAnnounce();
					yield return (object)new WaitForSeconds(Plugin.Cfg.RerollAnnounceLead.Value);
					SimulateAssignment(isReroll: true);
					yield break;
				}
				float deadline = Time.time + Plugin.Cfg.RerollWaitForRevives.Value;
				while (Time.time < deadline && CountAliveActors() < Plugin.Cfg.MinPlayers.Value)
				{
					yield return (object)new WaitForSeconds(2f);
				}
				bool postponedLogged = false;
				while (true)
				{
					if (!_rolesAssignedThisRun)
					{
						yield break;
					}
					if (CountAliveActors() < 2)
					{
						if (!postponedLogged)
						{
							Plugin.Log.LogWarning((object)"Only one player alive - re-roll postponed until someone is revived.");
							postponedLogged = true;
						}
						yield return (object)new WaitForSeconds(2f);
					}
					else
					{
						ImposterNet.SendRerollAnnounce();
						yield return (object)new WaitForSeconds(Plugin.Cfg.RerollAnnounceLead.Value);
						if (AssignRoles(isReroll: true))
						{
							break;
						}
					}
				}
				SetCeasefire(on: false);
			}
			finally
			{
				ImposterManager imposterManager = this;
				imposterManager._rerollRunning = false;
				imposterManager._rerollRoutine = null;
			}
		}

		private void ApplyReward()
		{
			Character localCharacter = Character.localCharacter;
			if (!((Object)(object)localCharacter == (Object)null) && localCharacter.refs != null && !((Object)(object)localCharacter.refs.afflictions == (Object)null))
			{
				localCharacter.refs.afflictions.ClearAllStatus(true, true);
				localCharacter.refs.afflictions.UpdateWeight();
				localCharacter.AddStamina(1f);
				ImposterUI.ShowAnnouncement(Loc.T("reward.title"), Loc.T("reward.sub"), positive: true);
				Plugin.Log.LogInfo((object)"Sole survivor reward applied.");
			}
		}

		public void OnEvent(EventData photonEvent)
		{
			switch (photonEvent.Code)
			{
			case 171:
				HandleRole(photonEvent);
				break;
			case 172:
				HandleKillReport(photonEvent);
				break;
			case 173:
				HandleReveal(photonEvent);
				break;
			case 174:
				HandleAbort(photonEvent.CustomData as string);
				break;
			case 175:
				HandleRerollAnnounce();
				break;
			case 176:
				ApplyReward();
				break;
			case 177:
				HandleToast(photonEvent);
				break;
			case 178:
				HandleWaiting(photonEvent);
				break;
			case 179:
				HandleAccuse(photonEvent);
				break;
			case 180:
				HandleExecute(photonEvent);
				break;
			case 181:
				HandleAccuseResult(photonEvent);
				break;
			case 182:
				HandleBookRequest(photonEvent.Sender);
				break;
			case 183:
				HandleBookGranted(photonEvent);
				break;
			case 170:
				HandleCeasefire(photonEvent);
				break;
			}
		}

		private void HandleRole(EventData photonEvent)
		{
			if (!ImposterNet.TryReadRole(photonEvent.CustomData, out var role, out var imposters, out var isReroll))
			{
				Plugin.Log.LogError((object)"Could not read the role event.");
			}
			else
			{
				ApplyRole(role, imposters, isReroll);
			}
		}

		private void ApplyRole(Role role, int[] imposters, bool isReroll)
		{
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			if (!isReroll && ImposterState.Active && ImposterState.LocalRole == role)
			{
				Plugin.Log.LogWarning((object)"Role was already set - ignoring duplicate event.");
				return;
			}
			bool isImposter = ImposterState.IsImposter;
			bool isSheriff = ImposterState.IsSheriff;
			if (isReroll)
			{
				ImposterState.ResetForReroll();
			}
			ImposterState.Ceasefire = false;
			ImposterState.Active = true;
			ImposterState.LocalRole = role;
			ImposterState.LastKillTime = -99999f;
			ImposterState.FellowImposters.Clear();
			if (role == Role.Imposter && imposters != null)
			{
				int num = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1));
				int[] array = imposters;
				foreach (int num2 in array)
				{
					if (num2 != num && !ImposterState.FellowImposters.Contains(num2))
					{
						ImposterState.FellowImposters.Add(num2);
					}
				}
			}
			if (!PhotonNetwork.IsMasterClient)
			{
				ImposterState.ImposterActors.Clear();
				if (role == Role.Imposter)
				{
					int[] array = imposters;
					foreach (int item in array)
					{
						ImposterState.ImposterActors.Add(item);
					}
				}
			}
			if (isImposter && role != Role.Imposter && Plugin.Cfg.TakeDaggerBackOnReroll.Value)
			{
				((MonoBehaviour)this).StartCoroutine(RemoveOwnDagger());
			}
			if (isSheriff && role != Role.Sheriff && (Object)(object)SheriffAbility.Instance != (Object)null)
			{
				SheriffAbility.Instance.Stow();
			}
			Scene activeScene = SceneManager.GetActiveScene();
			_roleSceneHandle = SceneHandle.op_Implicit(((Scene)(ref activeScene)).handle);
			Plugin.Log.LogInfo((object)("Role received: " + role.ToString() + (isReroll ? " (re-roll)" : "")));
			((MonoBehaviour)this).StartCoroutine(ShowRoleCardWhenReady(role, imposters, isReroll));
		}

		private IEnumerator ShowRoleCardWhenReady(Role role, int[] imposters, bool isReroll)
		{
			bool withIntro = !isReroll || Plugin.Cfg.IntroOnReroll.Value;
			ImposterUI.RoleCardPending = true;
			try
			{
				float guard = Time.realtimeSinceStartup + 60f;
				while ((LoadingScreenHandler.loading || (Object)(object)Character.localCharacter == (Object)null) && Time.realtimeSinceStartup < guard)
				{
					yield return null;
				}
				if (ImposterState.LocalRole == role)
				{
					Character localCharacter = Character.localCharacter;
					if (withIntro && (Object)(object)localCharacter != (Object)null && (Object)(object)localCharacter.data != (Object)null && localCharacter.data.dead)
					{
						withIntro = false;
					}
					ImposterUI.ShowRoleCard(role, imposters, withIntro, isReroll);
				}
			}
			finally
			{
				ImposterUI.RoleCardPending = false;
			}
		}

		private void HandleRerollAnnounce()
		{
			ImposterUI.ShowAnnouncement(Loc.T("reroll.title"), Loc.T("reroll.sub"), positive: false);
		}

		private void HandleToast(EventData photonEvent)
		{
			if (photonEvent.CustomData is object[] array && array.Length >= 2)
			{
				Toast(Loc.T((array[0] as string) ?? ""), (float)array[1]);
			}
		}

		private void HandleWaiting(EventData photonEvent)
		{
			if (photonEvent.CustomData is object[] array && array.Length >= 1)
			{
				ImposterState.WaitingForDaggerSlot = (bool)array[0];
			}
		}

		private void HandleAccuse(EventData photonEvent)
		{
			if (photonEvent.CustomData is object[] array && array.Length >= 1)
			{
				SubmitAccusation(photonEvent.Sender, (int)array[0]);
			}
		}

		public void SubmitAccusation(int sheriff, int target)
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (sheriff != _sheriffActor)
			{
				Plugin.Log.LogWarning((object)("Accusation from actor " + sheriff + ", who is not the sheriff - ignored."));
			}
			else
			{
				if (_sheriffShotsLeft <= 0)
				{
					return;
				}
				_sheriffShotsLeft--;
				bool flag = ImposterState.ImposterActors.Contains(target);
				Plugin.Log.LogInfo((object)("Sheriff " + NameOf(sheriff) + " accuses " + NameOf(target) + " -> " + (flag ? "imposter" : "innocent")));
				SendAccuseResultTo(sheriff, flag, _sheriffShotsLeft);
				if (flag)
				{
					if (!_correctCallsByActor.ContainsKey(sheriff))
					{
						_correctCallsByActor[sheriff] = 0;
					}
					_correctCallsByActor[sheriff]++;
					Execute(target, 0);
					if (Plugin.Cfg.SheriffAnnounceHit.Value)
					{
						ImposterNet.SendToastAll("sheriff.announce", 8f);
					}
				}
				else if (Plugin.Cfg.SheriffDiesOnMisfire.Value)
				{
					Execute(sheriff, 1);
				}
			}
		}

		private void Execute(int actor, byte reason)
		{
			if (ImposterState.IsDebugFake(actor))
			{
				ImposterState.DebugDummies.Remove(actor);
				Plugin.Log.LogInfo((object)("DEBUG: " + NameOf(actor) + " executed (reason " + reason + ")."));
			}
			else if (PhotonNetwork.LocalPlayer != null && actor == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				ApplyExecution(reason);
			}
			else
			{
				ImposterNet.SendExecute(actor, reason);
			}
		}

		private void SendAccuseResultTo(int actor, bool guilty, int shotsLeft)
		{
			if (PhotonNetwork.LocalPlayer != null && actor == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				ApplyAccuseResult(guilty, shotsLeft);
			}
			else
			{
				ImposterNet.SendAccuseResult(actor, guilty, shotsLeft);
			}
		}

		private void HandleBookGranted(EventData photonEvent)
		{
			if (photonEvent.CustomData is object[] array && array.Length >= 2 && Guid.TryParse(array[1] as string, out var result) && (Object)(object)SheriffAbility.Instance != (Object)null)
			{
				SheriffAbility.Instance.OnBookGranted((byte)array[0], result);
			}
		}

		private void HandleExecute(EventData photonEvent)
		{
			byte reason = (byte)((photonEvent.CustomData is object[] array && array.Length != 0) ? ((byte)array[0]) : 0);
			ApplyExecution(reason);
		}

		private void ApplyExecution(byte reason)
		{
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null)
			{
				return;
			}
			Toast(Loc.T((reason == 0) ? "sheriff.executed" : "sheriff.misfire.death"), 8f);
			Plugin.Log.LogInfo((object)("Executed by the sheriff (reason " + reason + ")."));
			try
			{
				localCharacter.DieInstantly();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Execution failed: " + ex.Message));
			}
		}

		private void HandleAccuseResult(EventData photonEvent)
		{
			if (photonEvent.CustomData is object[] array && array.Length >= 2)
			{
				ApplyAccuseResult((bool)array[0], (int)array[1]);
			}
		}

		private void ApplyAccuseResult(bool guilty, int shotsLeft)
		{
			ImposterState.SheriffShotsLeft = shotsLeft;
			if ((Object)(object)SheriffAbility.Instance != (Object)null)
			{
				SheriffAbility.Instance.Cancel();
			}
			if (guilty)
			{
				ImposterUI.ShowAnnouncement(Loc.T("sheriff.hit.title"), Loc.T("sheriff.hit.sub"), positive: true);
			}
			else if (!Plugin.Cfg.SheriffDiesOnMisfire.Value)
			{
				Toast(Loc.T("sheriff.misfire"), 6f);
			}
		}

		private void HandleKillReport(EventData photonEvent)
		{
			int victim = ((photonEvent.CustomData is object[] array && array.Length != 0) ? ((int)array[0]) : (-1));
			CountKill(photonEvent.Sender, victim);
		}

		private void CountKill(int killer, int victim)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				if (!_killsByActor.ContainsKey(killer))
				{
					_killsByActor[killer] = 0;
				}
				_killsByActor[killer]++;
				Plugin.Log.LogInfo((object)("Kill reported: " + NameOf(killer) + " -> " + NameOf(victim)));
			}
		}

		private void HandleReveal(EventData photonEvent)
		{
			if (ImposterNet.TryReadReveal(photonEvent.CustomData, out var outcome, out var imposterEntries, out var sheriffEntries))
			{
				string text = Loc.T(outcome switch
				{
					1 => "reveal.crewwon", 
					0 => "reveal.imposterwon", 
					_ => "reveal.nobodywon", 
				});
				string text2 = Loc.T("reveal.imposters", (imposterEntries.Length != 0) ? string.Join(", ", imposterEntries) : "-");
				text2 = text2 + "\n" + ((sheriffEntries.Length != 0) ? Loc.T("reveal.sheriffs", string.Join(", ", sheriffEntries)) : Loc.T("reveal.nosheriff"));
				ImposterState.Active = false;
				ImposterUI.ShowReveal(text, text2, outcome == 0);
				Plugin.Log.LogInfo((object)("Reveal shown: " + text));
			}
		}

		private void HandleAbort(string reason)
		{
			ImposterState.Reset();
			if (!string.IsNullOrEmpty(reason))
			{
				Plugin.Log.LogInfo((object)("Imposter mode aborted: " + reason));
				Toast(reason, 8f);
			}
		}

		public void RegisterLocalKill(Character victim)
		{
			ImposterState.LastKillTime = Time.time;
			ImposterState.LocalKills++;
			int num = -1;
			if ((Object)(object)victim != (Object)null && (Object)(object)((MonoBehaviourPun)victim).photonView != (Object)null && ((MonoBehaviourPun)victim).photonView.Owner != null)
			{
				num = ((MonoBehaviourPun)victim).photonView.Owner.ActorNumber;
			}
			if (PhotonNetwork.IsMasterClient && PhotonNetwork.LocalPlayer != null)
			{
				CountKill(PhotonNetwork.LocalPlayer.ActorNumber, num);
			}
			else
			{
				ImposterNet.ReportKill(num);
			}
			Toast(Loc.T("toast.kill", ImposterState.FormatTime(ImposterState.CooldownSeconds)), 6f);
			Plugin.Log.LogInfo((object)("Local kill registered (victim actor " + num + ")."));
		}

		public void OnGameEnded()
		{
			if (!PhotonNetwork.IsMasterClient || !_rolesAssignedThisRun)
			{
				return;
			}
			bool flag = false;
			bool flag2 = false;
			foreach (Character allCharacter in Character.AllCharacters)
			{
				if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)((MonoBehaviourPun)allCharacter).photonView == (Object)null) && ((MonoBehaviourPun)allCharacter).photonView.Owner != null && Character.CheckWinCondition(allCharacter))
				{
					if (ImposterState.ImposterActors.Contains(((MonoBehaviourPun)allCharacter).photonView.Owner.ActorNumber))
					{
						flag = true;
					}
					else
					{
						flag2 = true;
					}
				}
			}
			byte outcome = (byte)(flag2 ? 1 : ((!flag) ? 2 : 0));
			string[] array = (from a in _impostersThisRun.OrderByDescending(CountKills)
				select NameOf(a) + " (" + CountKills(a) + ")").ToArray();
			string[] array2 = (from a in _sheriffsThisRun.OrderByDescending(CountCalls)
				select NameOf(a) + " (" + CountCalls(a) + ")").ToArray();
			Plugin.Log.LogInfo((object)("Run ended, outcome " + outcome + " | Imposter: " + string.Join(", ", array) + " | Sheriffs: " + string.Join(", ", array2)));
			ImposterNet.SendReveal(outcome, array, array2);
		}

		private int CountKills(int actor)
		{
			if (!_killsByActor.TryGetValue(actor, out var value))
			{
				return 0;
			}
			return value;
		}

		private int CountCalls(int actor)
		{
			if (!_correctCallsByActor.TryGetValue(actor, out var value))
			{
				return 0;
			}
			return value;
		}

		private void SimulateAssignment(bool isReroll)
		{
			if (!PhotonNetwork.IsMasterClient || PhotonNetwork.LocalPlayer == null)
			{
				return;
			}
			if (PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.PlayerCount > 1)
			{
				Plugin.Log.LogWarning((object)("DEBUG: dummy simulation only works while alone in the lobby - " + PhotonNetwork.CurrentRoom.PlayerCount + " players present."));
				Toast(Loc.T("debug.dummies.alone"), 5f);
				return;
			}
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			if (!isReroll || ImposterState.DebugDummies.Count == 0)
			{
				ImposterState.DebugDummies.Clear();
				int num = Mathf.Clamp(Plugin.Cfg.DebugDummyCount.Value, 1, 11);
				for (int i = 1; i <= num; i++)
				{
					ImposterState.DebugDummies.Add(-100 - i);
				}
			}
			List<int> list = new List<int>(ImposterState.DebugDummies);
			List<int> list2 = new List<int> { actorNumber };
			list2.AddRange(list);
			DebugRole value = Plugin.Cfg.DebugForceRole.Value;
			int num2 = Mathf.Clamp(Plugin.Cfg.ImposterCount.Value, 1, Mathf.Max(1, list2.Count - 1));
			List<int> imposters;
			switch (value)
			{
			case DebugRole.Imposter:
				imposters = new List<int> { actorNumber };
				imposters.AddRange(list.OrderBy((int _) => Random.value).Take(num2 - 1));
				break;
			case DebugRole.Random:
				imposters = list2.OrderBy((int _) => Random.value).Take(num2).ToList();
				break;
			default:
				imposters = list.OrderBy((int _) => Random.value).Take(num2).ToList();
				break;
			}
			int num3 = -1;
			bool flag = Plugin.Cfg.EnableSheriff.Value && list2.Count >= Plugin.Cfg.SheriffMinPlayers.Value;
			List<int> list3 = list2.Where((int a) => !imposters.Contains(a)).ToList();
			if (value == DebugRole.Sheriff)
			{
				if (flag)
				{
					num3 = actorNumber;
				}
				else
				{
					Plugin.Log.LogWarning((object)("DEBUG: sheriff forced, but EnableSheriff is off or " + list2.Count + " players < SheriffMinPlayers - you become a scout."));
				}
			}
			else if (flag)
			{
				if (value == DebugRole.Scout)
				{
					list3.Remove(actorNumber);
				}
				if (list3.Count > 0)
				{
					num3 = list3[Random.Range(0, list3.Count)];
				}
			}
			ImposterState.ProtectionSeconds = Plugin.Cfg.ProtectionSeconds.Value;
			ImposterState.CooldownSeconds = Plugin.Cfg.CooldownSeconds.Value;
			ImposterState.InfiniteDagger = Plugin.Cfg.InfiniteDagger.Value;
			ImposterState.SilentKill = Plugin.Cfg.SilentKill.Value;
			ImposterState.HideFeedIndicator = Plugin.Cfg.HideFeedIndicator.Value;
			ImposterState.OnlyImposterCanKill = Plugin.Cfg.OnlyImposterCanKill.Value;
			ImposterState.ProtectionEndRunTime = ImposterState.RunTime + (isReroll ? Plugin.Cfg.RerollProtectionSeconds.Value : Plugin.Cfg.ProtectionSeconds.Value);
			ImposterState.ImposterActors.Clear();
			foreach (int item in imposters)
			{
				ImposterState.ImposterActors.Add(item);
				_impostersThisRun.Add(item);
			}
			_sheriffActor = num3;
			if (num3 >= 0 || ImposterState.IsDebugFake(num3))
			{
				_sheriffsThisRun.Add(num3);
			}
			_sheriffShotsLeft = Plugin.Cfg.SheriffShots.Value;
			_rolesAssignedThisRun = true;
			Role role = (imposters.Contains(actorNumber) ? Role.Imposter : ((num3 != actorNumber) ? Role.Crew : Role.Sheriff));
			ImposterState.SheriffShotsLeft = ((role == Role.Sheriff) ? _sheriffShotsLeft : 0);
			ImposterState.SheriffAimSeconds = Plugin.Cfg.SheriffAimSeconds.Value;
			Plugin.Log.LogInfo((object)("DEBUG simulation" + (isReroll ? " (re-roll)" : "") + ": " + list2.Count + " players | imposters: " + string.Join(", ", imposters.Select(NameOf).ToArray()) + " | Sheriff: " + ((num3 == -1) ? "-" : NameOf(num3)) + " | you: " + role));
			Toast(Loc.T("debug.dummies", list2.Count, Loc.T(role switch
			{
				Role.Sheriff => "role.sheriff", 
				Role.Imposter => "role.imposter", 
				_ => "role.crew", 
			})), 6f);
			if (!isReroll)
			{
				ImposterState.Active = false;
			}
			ApplyRole(role, (role == Role.Imposter) ? imposters.ToArray() : new int[0], isReroll);
			if (role == Role.Imposter && Plugin.Cfg.GiveDaggerOnStart.Value)
			{
				if (_grantRoutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_grantRoutine);
				}
				_grantRoutine = ((MonoBehaviour)this).StartCoroutine(GrantDaggersRoutine(new int[1] { actorNumber }));
			}
		}

		private unsafe static bool Pressed(Keyboard kb, Key key)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			KeyControl val = kb[key];
			if (val == null || !((ButtonControl)val).wasPressedThisFrame)
			{
				return false;
			}
			Plugin.Log.LogInfo((object)("Debug key " + ((object)(*(Key*)(&key))/*cast due to .constrained prefix*/).ToString() + " pressed."));
			return true;
		}

		private void HandleDebugKeys()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return;
			}
			if (Pressed(current, Plugin.Cfg.DebugKeyImposter.Value))
			{
				ImposterState.Active = true;
				ImposterState.LocalRole = Role.Imposter;
				ImposterState.ProtectionEndRunTime = ImposterState.RunTime + Plugin.Cfg.ProtectionSeconds.Value;
				ImposterState.LastKillTime = -99999f;
				if (PhotonNetwork.LocalPlayer != null)
				{
					ImposterState.ImposterActors.Add(PhotonNetwork.LocalPlayer.ActorNumber);
					_impostersThisRun.Add(PhotonNetwork.LocalPlayer.ActorNumber);
				}
				_rolesAssignedThisRun = true;
				ImposterUI.ShowRoleCard(Role.Imposter, ImposterState.ImposterActors.ToArray(), withIntro: true);
				Toast(Loc.T("debug.imposter"));
				if (PhotonNetwork.IsMasterClient && DaggerUtil.Resolved && PhotonNetwork.LocalPlayer != null)
				{
					((MonoBehaviour)this).StartCoroutine(GrantDaggersRoutine(new int[1] { PhotonNetwork.LocalPlayer.ActorNumber }));
				}
			}
			if (Pressed(current, Plugin.Cfg.DebugKeyDummies.Value) && PhotonNetwork.IsMasterClient)
			{
				SimulateAssignment(isReroll: false);
			}
			if (Pressed(current, Plugin.Cfg.DebugKeySheriff.Value))
			{
				ImposterState.Active = true;
				ImposterState.LocalRole = Role.Sheriff;
				ImposterState.SheriffShotsLeft = Plugin.Cfg.SheriffShots.Value;
				ImposterState.SheriffAimSeconds = Plugin.Cfg.SheriffAimSeconds.Value;
				if (PhotonNetwork.LocalPlayer != null)
				{
					_sheriffActor = PhotonNetwork.LocalPlayer.ActorNumber;
					_sheriffsThisRun.Add(_sheriffActor);
				}
				_sheriffShotsLeft = ImposterState.SheriffShotsLeft;
				_rolesAssignedThisRun = true;
				ImposterUI.ShowRoleCard(Role.Sheriff, new int[0], withIntro: true);
				Toast(Loc.T("debug.sheriff"));
			}
			if (Pressed(current, Plugin.Cfg.DebugKeySkip.Value))
			{
				ImposterState.ProtectionEndRunTime = 0f;
				ImposterState.LastKillTime = -99999f;
				Toast(Loc.T("debug.protection"));
			}
			if (Pressed(current, Plugin.Cfg.DebugKeyKill.Value))
			{
				Toast(Loc.T("debug.kill"));
				try
				{
					Character.Die();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Instakill failed: " + ex.Message));
				}
			}
			if (Pressed(current, Plugin.Cfg.DebugKeyReroll.Value) && PhotonNetwork.IsMasterClient)
			{
				Toast(Loc.T("debug.reroll"));
				Vector3 campfirePosition = (((Object)(object)Character.localCharacter != (Object)null) ? Character.localCharacter.Center : Vector3.zero);
				OnCampfireLit(advancesBiome: true, campfirePosition);
			}
		}
	}
	public static class ImposterNet
	{
		public const byte EvtRole = 171;

		public const byte EvtKill = 172;

		public const byte EvtReveal = 173;

		public const byte EvtAbort = 174;

		public const byte EvtReroll = 175;

		public const byte EvtReward = 176;

		public const byte EvtToast = 177;

		public const byte EvtWaiting = 178;

		public const byte EvtAccuse = 179;

		public const byte EvtExecute = 180;

		public const byte EvtAccuseResult = 181;

		public const byte EvtBookRequest = 182;

		public const byte EvtBookGranted = 183;

		public const byte EvtCeasefire = 170;

		private static readonly SendOptions Reliable = SendOptions.SendReliable;

		public const byte OutcomeImposter = 0;

		public const byte OutcomeScouts = 1;

		public const byte OutcomeNobody = 2;

		private static void Raise(byte code, object content, RaiseEventOptions options)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.InRoom)
			{
				PhotonNetwork.RaiseEvent(code, content, options, Reliable);
			}
		}

		public static void SendRole(int targetActor, Role role, int[] imposterActors, float protectionEndRunTime, bool isReroll, int sheriffShots, float sheriffAimSeconds)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			object[] content = new object[12]
			{
				(byte)role,
				protectionEndRunTime,
				ImposterState.ProtectionSeconds,
				ImposterState.CooldownSeconds,
				ImposterState.InfiniteDagger,
				ImposterState.SilentKill,
				ImposterState.HideFeedIndicator,
				ImposterState.OnlyImposterCanKill,
				imposterActors ?? new int[0],
				isReroll,
				sheriffShots,
				sheriffAimSeconds
			};
			RaiseEventOptions val = new RaiseEventOptions();
			val.TargetActors = new int[1] { targetActor };
			Raise(171, content, val);
		}

		public static bool TryReadRole(object data, out Role role, out int[] imposters, out bool isReroll)
		{
			role = Role.Unknown;
			imposters = new int[0];
			isReroll = false;
			if (!(data is object[] array) || array.Length < 12)
			{
				return false;
			}
			role = (Role)(byte)array[0];
			ImposterState.ProtectionEndRunTime = (float)array[1];
			ImposterState.ProtectionSeconds = (float)array[2];
			ImposterState.CooldownSeconds = (float)array[3];
			ImposterState.InfiniteDagger = (bool)array[4];
			ImposterState.SilentKill = (bool)array[5];
			ImposterState.HideFeedIndicator = (bool)array[6];
			ImposterState.OnlyImposterCanKill = (bool)array[7];
			imposters = (array[8] as int[]) ?? new int[0];
			isReroll = (bool)array[9];
			ImposterState.SheriffShotsLeft = (int)array[10];
			ImposterState.SheriffAimSeconds = (float)array[11];
			return true;
		}

		public static void ReportKill(int victimActor)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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)
			//IL_0025: Expected O, but got Unknown
			Raise(172, new object[1] { victimActor }, new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)2
			});
		}

		public static void SendAccuse(int targetActor)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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)
			//IL_0025: Expected O, but got Unknown
			Raise(179, new object[1] { targetActor }, new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)2
			});
		}

		public static void SendExecute(int targetActor, byte reason)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			object[] content = new object[1] { reason };
			RaiseEventOptions val = new RaiseEventOptions();
			val.TargetActors = new int[1] { targetActor };
			Raise(180, content, val);
		}

		public static void SendBookRequest()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			Raise(182, new object[0], new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)2
			});
		}

		public static void SendBookGranted(int targetActor, byte slotId, Guid ledgerId)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			object[] content = new object[2]
			{
				slotId,
				ledgerId.ToString()
			};
			RaiseEventOptions val = new RaiseEventOptions();
			val.TargetActors = new int[1] { targetActor };
			Raise(183, content, val);
		}

		public static void SendAccuseResult(int targetActor, bool guilty, int shotsLeft)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			object[] content = new object[2] { guilty, shotsLeft };
			RaiseEventOptions val = new RaiseEventOptions();
			val.TargetActors = new int[1] { targetActor };
			Raise(181, content, val);
		}

		public static void SendRerollAnnounce()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			Raise(175, new object[0], new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)1
			});
		}

		public static void SendCeasefire(bool on)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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)
			//IL_0025: Expected O, but got Unknown
			Raise(170, new object[1] { on }, new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)0
			});
		}

		public static void SendReward(int targetActor)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			object[] content = new object[0];
			RaiseEventOptions val = new RaiseEventOptions();
			val.TargetActors = new int[1] { targetActor };
			Raise(176, content, val);
		}

		public static void SendToast(int targetActor, string locKey, float seconds)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			object[] content = new object[2] { locKey, seconds };
			RaiseEventOptions val = new RaiseEventOptions();
			val.TargetActors = new int[1] { targetActor };
			Raise(177, content, val);
		}

		public static void SendToastAll(string locKey, float seconds)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			Raise(177, new object[2] { locKey, seconds }, new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)1
			});
		}

		public static void SendWaiting(int targetActor, bool waiting)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			object[] content = new object[1] { waiting };
			RaiseEventOptions val = new RaiseEventOptions();
			val.TargetActors = new int[1] { targetActor };
			Raise(178, content, val);
		}

		public static void SendReveal(byte outcome, string[] imposterEntries, string[] sheriffEntries)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			Raise(173, new object[3]
			{
				outcome,
				imposterEntries ?? new string[0],
				sheriffEntries ?? new string[0]
			}, new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)1
			});
		}

		public static bool TryReadReveal(object data, out byte outcome, out string[] imposterEntries, out string[] sheriffEntries)
		{
			outcome = 2;
			imposterEntries = new string[0];
			sheriffEntries = new string[0];
			if (!(data is object[] array) || array.Length < 3)
			{
				return false;
			}
			outcome = (byte)array[0];
			imposterEntries = (array[1] as string[]) ?? new string[0];
			sheriffEntries = (array[2] as string[]) ?? new string[0];
			return true;
		}

		public static void SendAbort(string reason)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			Raise(174, reason, new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)1
			});
		}

		public static List<int> GetActivePlayerActors()
		{
			List<int> list = new List<int>();
			if (!PhotonNetwork.InRoom)
			{
				return list;
			}
			Player[] playerList = PhotonNetwork.PlayerList;
			foreach (Player val in playerList)
			{
				if (val != null && !val.IsInactive)
				{
					list.Add(val.ActorNumber);
				}
			}
			return list;
		}
	}
	public enum Role : byte
	{
		Unknown,
		Crew,
		Imposter,
		Sheriff
	}
	public static class ImposterState
	{
		public static bool Active;

		public static Role LocalRole = Role.Unknown;

		public static readonly HashSet<int> ImposterActors = new HashSet<int>();

		public static float ProtectionEndRunTime;

		public static bool Ceasefire;

		public static float ProtectionSeconds = 180f;

		public static float CooldownSeconds = 60f;

		public static bool InfiniteDagger = true;

		public static bool SilentKill = true;

		public static bool HideFeedIndicator = true;

		public static bool OnlyImposterCanKill = true;

		public static float LastKillTime = -99999f;

		public static int LocalKills;

		public static bool WaitingForDaggerSlot;

		public static int SheriffShotsLeft;

		public static float SheriffAimSeconds = 1.5f;

		public static readonly List<int> FellowImposters = new List<int>();

		public const int DebugFakeBase = -100;

		public static readonly List<int> DebugDummies = new List<int>();

		public static bool IsImposter => LocalRole == Role.Imposter;

		public static bool IsSheriff => LocalRole == Role.Sheriff;

		public static float RunTime
		{
			get
			{
				RunManager instance = RunManager.Instance;
				if (!((Object)(object)instance != (Object)null))
				{
					return 0f;
				}
				return instance.TimeSinceRunStarted;
			}
		}

		public static bool ProtectionActive
		{
			get
			{
				if (Active)
				{
					if (!Ceasefire)
					{
						return RunTime < ProtectionEndRunTime;
					}
					return true;
				}
				return false;
			}
		}

		public static float ProtectionRemaining => Mathf.Max(0f, ProtectionEndRunTime - RunTime);

		public static float CooldownRemaining => Mathf.Max(0f, LastKillTime + CooldownSeconds - Time.time);

		public static bool CanStab
		{
			get
			{
				if (Active && IsImposter && !ProtectionActive)
				{
					return CooldownRemaining <= 0f;
				}
				return false;
			}
		}

		public static bool IsDebugFake(int actor)
		{
			return actor < -100;
		}

		public static string DebugFakeName(int actor)
		{
			return "Dummy " + (-100 - actor);
		}

		public static void Reset()
		{
			Active = false;
			LocalRole = Role.Unknown;
			ImposterActors.Clear();
			ProtectionEndRunTime = 0f;
			Ceasefire = false;
			LastKillTime = -99999f;
			LocalKills = 0;
			WaitingForDaggerSlot = false;
			SheriffShotsLeft = 0;
			FellowImposters.Clear();
			DebugDummies.Clear();
		}

		public static void ResetForReroll()
		{
			LocalRole = Role.Unknown;
			LastKillTime = -99999f;
			WaitingForDaggerSlot = false;
			FellowImposters.Clear();
		}

		public static string FormatTime(float seconds)
		{
			if (seconds < 0f)
			{
				seconds = 0f;
			}
			int num = Mathf.CeilToInt(seconds);
			return $"{num / 60}:{num % 60:00}";
		}
	}
	public enum HudCorner
	{
		TopLeft,
		TopRight
	}
	public enum RevealSide
	{
		Left,
		Right
	}
	public class ImposterUI : MonoBehaviour
	{
		public static bool RoleCardPending;

		private static readonly Color ImposterRed = Color32.op_Implicit(new Color32((byte)232, (byte)72, (byte)72, byte.MaxValue));

		private static readonly Color CrewGreen = Color32.op_Implicit(new Color32((byte)118, (byte)209, (byte)128, byte.MaxValue));

		private static readonly Color Warm = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)214, (byte)120, byte.MaxValue));

		private static readonly Color SheriffBlue = Color32.op_Implicit(new Color32((byte)108, (byte)176, (byte)240, byte.MaxValue));

		private GameObject _root;

		private RectTransform _badge;

		private TextMeshProUGUI _badgeRole;

		private TextMeshProUGUI _badgeStatus;

		private TextMeshProUGUI _badgeKills;

		private TextMeshProUGUI _badgeFellows;

		private RectTransform _announce;

		private TextMeshProUGUI _announceTitle;

		private TextMeshProUGUI _announceSub;

		private float _announceUntil;

		private RectTransform _toast;

		private TextMeshProUGUI _toastText;

		private RectTransform _reveal;

		private Image _revealBackdrop;

		private TextMeshProUGUI _revealTitle;

		private TextMeshProUGUI _revealBody;

		private float _revealUntil;

		private TMP_FontAsset _font;

		private HudCorner _builtCorner;

		private float _builtScale;

		private bool _builtBold;

		private float _builtOutline;

		private RevealSide _builtRevealSide;

		public static ImposterUI Instance { get; private set; }

		public static void ShowRoleCard(Role role, int[] imposters, bool withIntro, bool isReroll = false)
		{
			if (!((Object)(object)Instance == (Object)null))
			{
				Compose(role, imposters, out var title, out var sub);
				if (!withIntro || !Plugin.Cfg.UseIntroSequence.Value || !((Object)(object)IntroSequence.Instance != (Object)null) || !IntroSequence.Instance.Play(role, imposters, title, sub, isReroll))
				{
					ShowPlainRoleCard(role, title, sub);
				}
			}
		}

		public static void ShowPlainRoleCard(Role role, string title, string sub)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Instance == (Object)null))
			{
				Instance.Announce(title, sub, RoleColor(role), Plugin.Cfg.RoleCardSeconds.Value);
			}
		}

		public static Color RoleColor(Role role)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			return (Color)(role switch
			{
				Role.Imposter => ImposterRed, 
				Role.Sheriff => SheriffBlue, 
				_ => CrewGreen, 
			});
		}

		public static string FellowNames()
		{
			if (!Plugin.Cfg.ShowFellowImposters.Value)
			{
				return null;
			}
			if (!ImposterState.IsImposter || ImposterState.FellowImposters.Count == 0)
			{
				return null;
			}
			List<string> list = new List<string>();
			foreach (int fellowImposter in ImposterState.FellowImposters)
			{
				list.Add(ActorName(fellowImposter));
			}
			return string.Join(", ", list.ToArray());
		}

		private static string ActorName(int actor)
		{
			if (ImposterState.IsDebugFake(actor))
			{
				return ImposterState.DebugFakeName(actor);
			}
			Character val = default(Character);
			if (PlayerHandler.TryGetCharacter(actor, ref val) && (Object)(object)val != (Object)null)
			{
				return val.characterName;
			}
			Room currentRoom = PhotonNetwork.CurrentRoom;
			Player val2 = ((currentRoom != null) ? currentRoom.GetPlayer(actor, false) : null);
			if (val2 == null)
			{
				return "Scout #" + actor;
			}
			return val2.NickName;
		}

		private static void Compose(Role role, int[] imposters, out string title, out string sub)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (role == Role.Sheriff)
			{
				title = Loc.T("card.sheriff.title");
				sub = Loc.T("card.sheriff.sub", ImposterState.SheriffShotsLeft, Plugin.Cfg.SheriffKey.Value);
				if (Plugin.Cfg.SheriffDiesOnMisfire.Value)
				{
					sub = sub + "\n" + Loc.T("card.sheriff.warn");
				}
				return;
			}
			bool flag = role == Role.Imposter;
			title = Loc.T(flag ? "card.imposter.title" : "card.crew.title");
			if (!flag)
			{
				sub = Loc.T("card.crew.sub");
				return;
			}
			sub = ((imposters != null && imposters.Length > 1) ? Loc.T("card.imposter.sub.multi", imposters.Length) : Loc.T("card.imposter.sub"));
			string text = FellowNames();
			if (text != null)
			{
				sub = sub + "\n" + Loc.T("card.imposter.fellows", text);
			}
			sub = sub + "\n" + Loc.T(Plugin.Cfg.GiveDaggerAfterProtection.Value ? "card.imposter.hint.later" : "card.imposter.hint", ImposterState.FormatTime(ImposterState.ProtectionRemaining), ImposterState.FormatTime(ImposterState.CooldownSeconds));
		}

		public static void ShowAnnouncement(string title, string sub, bool positive)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Instance == (Object)null))
			{
				Instance.Announce(title, sub, positive ? CrewGreen : Warm, Plugin.Cfg.RoleCardSeconds.Value);
			}
		}

		public static void ShowReveal(string title, string body, bool imposterWon)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Instance == (Object)null))
			{
				Instance.EnsureBuilt();
				if (!((Object)(object)Instance._revealTitle == (Object)null))
				{
					((TMP_Text)Instance._revealTitle).text = title;
					((Graphic)Instance._revealTitle).color = (imposterWon ? ImposterRed : CrewGreen);
					((TMP_Text)Instance._revealBody).text = body;
					Instance._revealUntil = Time.time + Plugin.Cfg.RevealSeconds.Value;
					((Component)Instance._reveal).gameObject.SetActive(true);
				}
			}
		}

		private void Announce(string title, string sub, Color color, float seconds)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			EnsureBuilt();
			if (!((Object)(object)_announceTitle == (Object)null))
			{
				((TMP_Text)_announceTitle).text = title;
				((Graphic)_announceTitle).color = color;
				((TMP_Text)_announceSub).text = sub;
				_announceUntil = Time.time + seconds;
				((Component)_announce).gameObject.SetActive(true);
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		public static TMP_FontAsset GameFont()
		{
			GUIManager instance = GUIManager.instance;
			if ((Object)(object)instance != (Object)null)
			{
				if ((Object)(object)instance.interactPromptText != (Object)null && (Object)(object)((TMP_Text)instance.interactPromptText).font != (Object)null)
				{
					return ((TMP_Text)instance.interactPromptText).font;
				}
				if ((Object)(object)instance.heroText != (Object)null && (Object)(object)((TMP_Text)instance.heroText).font != (Object)null)
				{
					return ((TMP_Text)instance.heroText).font;
				}
			}
			return TMP_Settings.defaultFontAsset;
		}

		private bool SettingsChanged()
		{
			if (_builtCorner == Plugin.Cfg.HudCorner.Value && Mathf.Approximately(_builtScale, Plugin.Cfg.HudScale.Value) && _builtBold == Plugin.Cfg.HudBold.Value && Mathf.Approximately(_builtOutline, Plugin.Cfg.HudOutline.Value))
			{
				return _builtRevealSide != Plugin.Cfg.RevealSide.Value;
			}
			return true;
		}

		private void EnsureBuilt()
		{
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Expected O, but got Unknown
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_root != (Object)null && SettingsChanged())
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
			}
			if ((Object)(object)_root != (Object)null)
			{
				TMP_FontAsset val = GameFont();
				if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)_font)
				{
					_font = val;
					TextMeshProUGUI[] array = (TextMeshProUGUI[])(object)new TextMeshProUGUI[9] { _badgeRole, _badgeStatus, _badgeKills, _badgeFellows, _announceTitle, _announceSub, _toastText, _revealTitle, _revealBody };
					foreach (TextMeshProUGUI t in array)
					{
						StyleText(t);
					}
				}
			}
			else
			{
				_font = GameFont();
				_builtCorner = Plugin.Cfg.HudCorner.Value;
				_builtScale = Plugin.Cfg.HudScale.Value;
				_builtBold = Plugin.Cfg.HudBold.Value;
				_builtOutline = Plugin.Cfg.HudOutline.Value;
				_builtRevealSide = Plugin.Cfg.RevealSide.Value;
				float builtScale = _builtScale;
				_root = new GameObject("PeakImposterHUD");
				((Object)_root).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)_root);
				Canvas obj = _root.AddComponent<Canvas>();
				obj.renderMode = (RenderMode)0;
				obj.sortingOrder = 30000;
				CanvasScaler obj2 = _root.AddComponent<CanvasScaler>();
				obj2.uiScaleMode = (ScaleMode)1;
				obj2.referenceResolution = new Vector2(1920f, 1080f);
				obj2.screenMatchMode = (ScreenMatchMode)0;
				obj2.matchWidthOrHeight = 0.5f;
				BuildBadge(builtScale);
				BuildAnnounce(builtScale);
				BuildToast(builtScale);
				BuildReveal(builtScale);
			}
		}

		private RectTransform MakeContainer(string name, Vector2 anchor, Vector2 offset, Vector2 size)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(_root.transform, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = anchor;
			component.anchorMax = anchor;
			component.pivot = anchor;
			component.anchoredPosition = offset;
			component.sizeDelta = size;
			return component;
		}

		private TextMeshProUGUI MakeLine(RectTransform parent, string name, float y, float height, float fontSize, TextAlignmentOptions align)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(TextMeshProUGUI)
			});
			val.transform.SetParent((Transform)(object)parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
			component.offsetMin = new Vector2(0f, 0f);
			component.offsetMax = new Vector2(0f, 0f);
			component.anchoredPosition = new Vector2(0f, 0f - y);
			component.sizeDelta = new Vector2(0f, height);
			TextMeshProUGUI component2 = val.GetComponent<TextMeshProUGUI>();
			((TMP_Text)component2).fontSize = fontSize;
			((TMP_Text)component2).alignment = align;
			((Graphic)component2).color = Color.white;
			((Graphic)component2).raycastTarget = false;
			((TMP_Text)component2).textWrappingMode = (TextWrappingModes)1;
			((TMP_Text)component2).overflowMode = (TextOverflowModes)0;
			StyleText(component2);
			return component2;
		}

		private void StyleText(TextMeshProUGUI t)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)t == (Object)null)
			{
				return;
			}
			if ((Object)(object)_font != (Object)null)
			{
				((TMP_Text)t).font = _font;
			}
			((TMP_Text)t).fontStyle = (FontStyles)(Plugin.Cfg.HudBold.Value ? 1 : 0);
			float value = Plugin.Cfg.HudOutline.Value;
			if (value <= 0f)
			{
				return;
			}
			try
			{
				((TMP_Text)t).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)220);
				((TMP_Text)t).outlineWidth = value;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Text outline not supported: " + ex.Message));
			}
		}

		private void BuildBadge(float s)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			bool flag = _builtCorner == HudCorner.TopRight;
			Vector2 anchor = default(Vector2);
			((Vector2)(ref anchor))..ctor(flag ? 1f : 0f, 1f);
			_badge = MakeContainer("Badge", anchor, new Vector2(flag ? (-34f) : 34f, -30f), new Vector2(560f * s, 170f * s));
			TextAlignmentOptions align = (TextAlignmentOptions)(flag ? 260 : 257);
			_badgeRole = MakeLine(_badge, "Role", 0f, 46f * s, 30f * s, align);
			_badgeStatus = MakeLine(_badge, "Status", 44f * s, 36f * s, 23f * s, align);
			_badgeKills = MakeLine(_badge, "Kills", 80f * s, 30f * s, 18f * s, align);
			_badgeFellows = MakeLine(_badge, "Fellows", 108f * s, 30f * s, 18f * s, align);
			((Graphic)_badgeFellows).color = ImposterRed;
			((Component)_badge).gameObject.SetActive(false);
		}

		private void BuildAnnounce(float s)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			_announce = MakeContainer("Announce", new Vector2(0.5f, 1f), new Vector2(0f, -200f), new Vector2(1180f * s, 230f * s));
			_announceTitle = MakeLine(_announce, "Title", 0f, 60f * s, 40f * s, (TextAlignmentOptions)258);
			_announceSub = MakeLine(_announce, "Sub", 66f * s, 150f * s, 22f * s, (TextAlignmentOptions)258);
			((Component)_announce).gameObject.SetActive(false);
		}

		private void BuildToast(float s)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			_toast = MakeContainer("Toast", new Vector2(0.5f, 0f), new Vector2(0f, 210f), new Vector2(1180f * s, 90f * s));
			_toastText = MakeLine(_toast, "Text", 0f, 90f * s, 23f * s, (TextAlignmentOptions)258);
			((Graphic)_toastText).color = Warm;
			((Component)_toast).gameObject.SetActive(false);
		}

		private void BuildReveal(float s)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			bool flag = _builtRevealSide == RevealSide.Right;
			Vector2 anchor = default(Vector2);
			((Vector2)(ref anchor))..ctor(flag ? 1f : 0f, 0.5f);
			_reveal = MakeContainer("Reveal", anchor, new Vector2(flag ? (-40f) : 40f, 0f), new Vector2(540f * s, 260f * s));
			GameObject val = new GameObject("Backdrop", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent((Transform)(object)_reveal, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = new Vector2(-18f, -14f);
			component.offsetMax = new Vector2(18f, 14f);
			_revealBackdrop = val.GetComponent<Image>();
			((Graphic)_revealBackdrop).raycastTarget = false;
			TextAlignmentOptions align = (TextAlignmentOptions)(flag ? 260 : 257);
			_revealTitle = MakeLine(_reveal, "Title", 0f, 52f * s, 30f * s, align);
			_revealBody = MakeLine(_reveal, "Body", 56f * s, 200f * s, 20f * s, align);
			((Component)_reveal).gameObject.SetActive(false);
		}

		private void UpdateReveal()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if (!((Component)_reveal).gameObject.activeSelf)
			{
				return;
			}
			if (Time.time > _revealUntil)
			{
				((Component)_reveal).gameObject.SetActive(false);
				return;
			}
			float num = Mathf.Clamp01(_revealUntil - Time.time);
			((TMP_Text)_revealTitle).alpha = num;
			((TMP_Text)_revealBody).alpha = num;
			float value = Plugin.Cfg.RevealBackgroundAlpha.Value;
			((Behaviour)_revealBackdrop).enabled = value > 0f;
			if (((Behaviour)_revealBackdrop).enabled)
			{
				((Graphic)_revealBackdrop).color = new Color(0f, 0f, 0f, value * num);
			}
		}

		private void LateUpdate()
		{
			if (!Plugin.Cfg.ShowHud.Value)
			{
				if ((Object)(object)_root != (Object)null)
				{
					_root.SetActive(false);
				}
				return;
			}
			if (IntroSequence.IsPlaying || RoleCardPending || LoadingScreenHandler.loading)
			{
				if ((Object)(object)_root != (Object)null && _root.activeSelf)
				{
					_root.SetActive(false);
				}
				return;
			}
			EnsureBuilt();
			if (!((Object)(object)_root == (Object)null))
			{
				if (!_root.activeSelf)
				{
					_root.SetActive(true);
				}
				UpdateBadge();
				UpdateAnnounce();
				UpdateToast();
				UpdateReveal();
			}
		}

		private void UpdateBadge()
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			bool flag = ImposterState.Active && ImposterState.LocalRole != Role.Unknown && (Object)(object)Character.localCharacter != (Object)null;
			if (flag && ImposterState.LocalRole == Role.Crew && !Plugin.Cfg.ShowRoleBadgeForCrew.Value)
			{
				flag = false;
			}
			if (!flag)
			{
				if (((Component)_badge).gameObject.activeSelf)
				{
					((Component)_badge).gameObject.SetActive(false);
				}
				return;
			}
			if (!((Component)_badge).gameObject.activeSelf)
			{
				((Component)_badge).gameObject.SetActive(true);
			}
			bool isImposter = ImposterState.IsImposter;
			bool isSheriff = ImposterState.IsSheriff;
			((TMP_Text)_badgeRole).text = Loc.T(isImposter ? "role.imposter" : (isSheriff ? "role.sheriff" : "role.crew"));
			((Graphic)_badgeRole).color = (isImposter ? ImposterRed : (isSheriff ? SheriffBlue : CrewGreen));
			string text;
			Color color;
			if (ImposterState.Ceasefire)
			{
				text = Loc.T("hud.ceasefire");
				color = CrewGreen;
			}
			else if (ImposterState.ProtectionActive)
			{
				text = Loc.T("hud.protection", ImposterState.FormatTime(ImposterState.ProtectionRemaining));
				color = CrewGreen;
			}
			else if (isSheriff && SheriffBook.OwnLedgerSlot() >= 0)
			{
				text = Loc.T("hud.sheriff.stow");
				color = Warm;
			}
			else if (isSheriff)
			{
				bool flag2 = ImposterState.SheriffShotsLeft > 0;
				text = (flag2 ? Loc.T("hud.sheriff.ready", ImposterState.SheriffShotsLeft, Plugin.Cfg.SheriffKey.Value) : Loc.T("hud.sheriff.spent"));
				color = (flag2 ? SheriffBlue : Color.white);
			}
			else if (!isImposter)
			{
				text = "";
				color = Color.white;
			}
			else if (ImposterState.WaitingForDaggerSlot)
			{
				text = Loc.T("hud.nodagger");
				color = Warm;
			}
			else if (ImposterState.CooldownRemaining > 0f)
			{
				text = Loc.T("hud.cooldown", ImposterState.FormatTime(ImposterState.CooldownRemaining));
				color = Warm;
			}
			else
			{
				text = Loc.T("hud.ready");
				color = ImposterRed;
			}
			((TMP_Text)_badgeStatus).text = text;
			((Graphic)_badgeStatus).color = color;
			((Component)_badgeStatus).gameObject.SetActive(!string.IsNullOrEmpty(text));
			bool flag3 = isImposter && ImposterState.LocalKills > 0;
			((Component)_badgeKills).gameObject.SetActive(flag3);
			if (flag3)
			{
				((TMP_Text)_badgeKills).text = Loc.T("hud.kills", ImposterState.LocalKills);
			}
			string text2 = FellowNames();
			((Component)_badgeFellows).gameObject.SetActive(text2 != null);
			if (text2 != null)
			{
				((TMP_Text)_badgeFellows).text = Loc.T("hud.fellows", text2);
			}
		}

		private void UpdateAnnounce()
		{
			if (((Component)_announce).gameObject.activeSelf)
			{
				if (Time.time > _announceUntil)
				{
					((Component)_announce).gameObject.SetActive(false);
					return;
				}
				float alpha = Mathf.Clamp01(_announceUntil - Time.time);
				((TMP_Text)_announceTitle).alpha = alpha;
				((TMP_Text)_announceSub).alpha = alpha;
			}
		}

		private void UpdateToast()
		{
			ImposterManager instance = ImposterManager.Instance;
			if (!((Object)(object)instance != (Object)null) || !(Time.time <= instance.ToastUntil) || string.IsNullOrEmpty(instance.ToastText))
			{
				if (((Component)_toast).gameObject.activeSelf)
				{
					((Component)_toast).gameObject.SetActive(false);
				}
				return;
			}
			if (!((Component)_toast).gameObject.activeSelf)
			{
				((Component)_toast).gameObject.SetActive(true);
			}
			((TMP_Text)_toastText).text = instance.ToastText;
			((TMP_Text)_toastText).alpha = Mathf.Clamp01(instance.ToastUntil - Time.time);
		}
	}
	public class IntroSequence : MonoBehaviour
	{
		private class CardTag : MonoBehaviour
		{
			public int Actor;
		}

		private sealed class CardEntry
		{
			public readonly int Actor;

			public readonly string Name;

			public readonly RenderTexture Photo;

			public CardEntry(int actor, string name, RenderTexture photo)
			{
				Actor = actor;
				Name = name;
				Photo = photo;
			}
		}

		private static readonly Color ImposterRed = Color32.op_Implicit(new Color32((byte)232, (byte)72, (byte)72, byte.MaxValue));

		private static readonly Color CrewGreen = Color32.op_Implicit(new Color32((byte)118, (byte)209, (byte)128, byte.MaxValue));

		private static readonly Color SheriffBlue = Color32.op_Implicit(new Color32((byte)108, (byte)176, (byte)240, byte.MaxValue));

		private static readonly Color Ink = Color32.op_Implicit(new Color32((byte)233, (byte)224, (byte)205, byte.MaxValue));

		private GameObject _root;

		private CanvasGroup _group;

		private RectTransform _cardRow;

		private TextMeshProUGUI _roleTitle;

		private TextMeshProUGUI _roleSub;

		private readonly List<GameObject> _cards = new List<GameObject>();

		private readonly List<Canvas> _hiddenCanvases = new List<Canvas>();

		private Dictionary<int, RenderTexture> _textures;

		private Coroutine _running;

		public static IntroSequence Instance { get; private set; }

		public static bool IsPlaying { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			RestoreGameUi();
			PortraitStudio.Release(_textures);
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			IsPlaying = false;
		}

		public bool Play(Role role, int[] imposters, string title, string sub, bool isReroll = false)
		{
			if (!PortraitStudio.IsAvailable)
			{
				return false;
			}
			if (!PhotonNetwork.InRoom)
			{
				return false;
			}
			if (_running != null)
			{
				((MonoBehaviour)this).StopCoroutine(_running);
			}
			_running = ((MonoBehaviour)this).StartCoroutine(Run(role, title, sub, isReroll));
			return true;
		}

		public void Stop()
		{
			if (_running != null)
			{
				((MonoBehaviour)this).StopCoroutine(_running);
			}
			_running = null;
			IsPlaying = false;
			RestoreGameUi();
			if ((Object)(object)_group != (Object)null)
			{
				_group.alpha = 0f;
			}
			if ((Object)(object)_root != (Object)null)
			{
				_root.SetActive(false);
			}
		}

		private void HideGameUi()
		{
			RestoreGameUi();
			if (!Plugin.Cfg.HideGameUiDuringIntro.Value)
			{
				return;
			}
			Canvas[] array;
			try
			{
				array = Object.FindObjectsByType<Canvas>((FindObjectsInactive)0, (FindObjectsSortMode)0);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not hide the game UI: " + ex.Message));
				return;
			}
			Canvas[] array2 = array;
			foreach (Canvas val in array2)
			{
				if (!((Object)(object)val == (Object)null) && ((Behaviour)val).enabled && !((Object)((Component)val).transform.root).name.StartsWith("PeakImposter"))
				{
					((Behaviour)val).enabled = false;
					_hiddenCanvases.Add(val);
				}
			}
		}

		private void RestoreGameUi()
		{
			foreach (Canvas hiddenCanvase in _hiddenCanvases)
			{
				if ((Object)(object)hiddenCanvase != (Object)null)
				{
					((Behaviour)hiddenCanvase).enabled = true;
				}
			}
			_hiddenCanvases.Clear();
		}

		private IEnumerator Run(Role role, string title, string sub, bool isReroll)
		{
			IsPlaying = true;
			float guard = Time.realtimeSinceStartup + 30f;
			while (LoadingScreenHandler.loading && Time.realtimeSinceStartup < guard)
			{
				yield return null;
			}
			yield return (object)new WaitForSeconds(Plugin.Cfg.IntroDelaySeconds.Value);
			HideGameUi();
			try
			{
				List<Player> players = new List<Player>();
				Player[] playerList = PhotonNetwork.PlayerList;
				Character val2 = default(Character);
				foreach (Player val in playerList)
				{
					if (val != null && !val.IsInactive && (!isReroll || !PlayerHandler.TryGetCharacter(val.ActorNumber, ref val2) || !((Object)(object)val2 != (Object)null) || !((Object)(object)val2.data != (Object)null) || !val2.data.dead))
					{
						players.Add(val);
					}
				}
				Dictionary<int, RenderTexture> shots = null;
				yield return PortraitStudio.Capture(players, delegate(Dictionary<int, RenderTexture> result)
				{
					shots = result;
				});
				if (shots == null)
				{
					RestoreGameUi();
					IsPlaying = false;
					_running = null;
					ImposterUI.ShowPlainRoleCard(role, title, sub);
					yield break;
				}
				PortraitStudio.Release(_textures);
				_textures = shots;
				Build();
				List<CardEntry> list = new List<CardEntry>();
				foreach (Player item in players)
				{
					if (item != null && shots.TryGetValue(item.ActorNumber, out var value))
					{
						list.Add(new CardEntry(item.ActorNumber, NameOf(item), value));
					}
				}
				int key = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1));
				if (shots.TryGetValue(key, out var value2))
				{
					foreach (int debugDummy in ImposterState.DebugDummies)
					{
						list.Add(new CardEntry(debugDummy, ImposterState.DebugFakeName(debugDummy), value2));
					}
				}
				BuildCards(list);
				int localActor = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : (-1));
				Color color = (Color)(role switch
				{
					Role.Sheriff => SheriffBlue, 
					Role.Imposter => ImposterRed, 
					_ => CrewGreen, 
				});
				((TMP_Text)_roleTitle).text = title;
				((Graphic)_roleTitle).color = color;
				((TMP_Text)_roleSub).text = sub;
				_root.SetActive(true);
				yield return Fade(_group, 0f, 1f, 0.4f);
				yield return (object)new WaitForSeconds(Plugin.Cfg.IntroGroupSeconds.Value);
				HashSet<int> hashSet = new HashSet<int>();
				if (role == Role.Imposter && Plugin.Cfg.ShowFellowImposters.Value)
				{
					foreach (int fellowImposter in ImposterState.FellowImposters)
					{
						hashSet.Add(fellowImposter);
					}
				}
				List<CanvasGroup> others = new List<CanvasGroup>();
				List<RectTransform> list2 = new List<RectTransform>();
				RectTransform mine = null;
				foreach (GameObject card in _cards)
				{
					int actor = card.GetComponent<CardTag>().Actor;
					if (actor == localActor)
					{
						mine = card.GetComponent<RectTransform>();
					}
					else if (hashSet.Contains(actor))
					{
						list2.Add(card.GetComponent<RectTransform>());
					}
					else
					{
						others.Add(card.GetComponent<CanvasGroup>());
					}
				}
				List<RectTransform> kept = new List<RectTransform>();
				if ((Object)(object)mine != (Object)null)
				{
					kept.Add(mine);
				}
				for (int num = 0; num < list2.Count; num++)
				{
					if (num % 2 == 0)
					{
						kept.Add(list2[num]);
					}
					else
					{
						kept.Insert(0, list2[num]);
					}
				}
				Dictionary<RectTransform, Vector2> starts = new Dictionary<RectTransform, Vector2>();
				Dictionary<RectTransform, Vector2> targets = new Dictionary<RectTransform, Vector2>();
				float num2 = 0f;
				foreach (RectTransform item2 in kept)
				{
					num2 += item2.sizeDelta.x * (((Object)(object)item2 == (Object)(object)mine) ? 1.35f : 1f);
				}
				num2 += 40f * (float)Mathf.Max(0, kept.Count - 1);
				float num3 = (0f - num2) * 0.5f;
				foreach (RectTransform item3 in kept)
				{
					float num4 = item3.sizeDelta.x * (((Object)(object)item3 == (Object)(object)mine) ? 1.35f : 1f);
					starts[item3] = item3.anchoredPosition;
					targets[item3] = new Vector2(num3 + num4 * 0.5f, 0f);
					num3 += num4 + 40f;
				}
				List<TextMeshProUGUI> tags = new List<TextMeshProUGUI>();
				TextMeshProUGUI val5 = default(TextMeshProUGUI);
				foreach (RectTransform item4 in list2)
				{
					TextMeshProUGUI val3 = MakeText((Transform)(object)item4, "FellowTag", 22f, (TextAlignmentOptions)1026);
					RectTransform rectTransform = ((TMP_Text)val3).rectTransform;
					rectTransform.anchorMin = new Vector2(0.5f, 1f);
					rectTransform.anchorMax = new Vector2(0.5f, 1f);
					rectTransform.pivot = new Vector2(0.5f, 0f);
					rectTransform.anchoredPosition = new Vector2(0f, 6f);
					rectTransform.sizeDelta = new Vector2(item4.sizeDelta.x + 40f, 32f);
					((TMP_Text)val3).text = Loc.T("intro.fellow");
					((Graphic)val3).color = ImposterRed;
					((TMP_Text)val3).alpha = 0f;
					tags.Add(val3);
					Transform val4 = ((Transform)item4).Find("Name");
					if ((Object)(object)val4 != (Object)null && ((Component)val4).TryGetComponent<TextMeshProUGUI>(ref val5))
					{
						((Graphic)val5).color = ImposterRed;
					}
				}
				float t = 0f;
				float move = 0.6f;
				while (t < move)
				{
					t += Time.deltaTime;
					float num5 = Mathf.SmoothStep(0f, 1f, t / move);
					foreach (CanvasGroup item5 in others)
					{
						item5.alpha = 1f - num5;
					}
					foreach (TextMeshProUGUI item6 in tags)
					{
						((TMP_Text)item6).alpha = num5;
					}
					foreach (RectTransform item7 in kept)
					{
						item7.anchoredPosition = Vector2.Lerp(starts[item7], targets[item7], num5);
						if ((Object)(object)item7 == (Object)(object)mine)
						{
							((Transform)item7).localScale = Vector3.one * Mathf.Lerp(1f, 1.35f, num5);
						}
					}
					yield return null;
				}
				foreach (CanvasGroup item8 in others)
				{
					((Component)item8).gameObject.SetActive(false);
				}
				yield return FadeText(1f, 0.35f);
				yield return (object)new WaitForSeconds(Plugin.Cfg.IntroRoleSeconds.Value);
				yield return Fade(_group, 1f, 0f, 0.5f);
				_root.SetActive(false);
			}
			finally
			{
				IntroSequence introSequence = this;
				introSequence.RestoreGameUi();
				IsPlaying = false;
				introSequence._running = null;
			}
		}

		private IEnumerator Fade(CanvasGroup group, float from, float to, float duration)
		{
			float t = 0f;
			while (t < duration)
			{
				t += Time.deltaTime;
				group.alpha = Mathf.Lerp(from, to, t / duration);
				yield return null;
			}
			group.alpha = to;
		}

		private IEnumerator FadeText(float to, float duration)
		{
			float t = 0f;
			while (t < duration)
			{
				t += Ti