Decompiled source of VoidCoinFlip v1.0.1

VoidCoinFlip.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RoR2;
using RoR2.ExpansionManagement;
using RoR2.UI;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.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 VoidCoinFlip
{
	internal static class FuelArrayDeath
	{
		private const string FuelArrayName = "QuestVolatileBattery";

		private const string MonitorStateName = "EntityStates.QuestVolatileBattery.Monitor";

		private const string CountDownStateName = "EntityStates.QuestVolatileBattery.CountDown";

		public static IEnumerator Run(IEnumerable<CharacterBody> playerBodies)
		{
			if (!NetworkServer.active)
			{
				yield break;
			}
			List<CharacterBody> bodies = playerBodies.Where((CharacterBody b) => Object.op_Implicit((Object)(object)b) && Object.op_Implicit((Object)(object)b.inventory)).ToList();
			EquipmentIndex val = EquipmentCatalog.FindEquipmentIndex("QuestVolatileBattery");
			Type countDownType = AccessTools.TypeByName("EntityStates.QuestVolatileBattery.CountDown");
			if ((int)val == -1 || countDownType == null)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)"Couldn't find the Fuel Array; falling back to a plain death.");
				KillAll(bodies);
				yield break;
			}
			Dictionary<Inventory, EquipmentIndex> previous = new Dictionary<Inventory, EquipmentIndex>();
			foreach (CharacterBody item in bodies)
			{
				previous[item.inventory] = item.inventory.currentEquipmentIndex;
				item.inventory.SetEquipmentIndex(val);
			}
			yield return (object)new WaitForSeconds(0.5f);
			int num = 0;
			foreach (CharacterBody item2 in bodies)
			{
				if (Object.op_Implicit((Object)(object)item2))
				{
					if (StartCountdown(item2, countDownType))
					{
						num++;
					}
					else
					{
						ForceBelowThreshold(item2);
					}
				}
			}
			VoidCoinFlipPlugin.Log.LogInfo((object)$"Fuel Array countdown started on {num}/{bodies.Count} player(s).");
			float num2 = ((AccessTools.Field(countDownType, "duration")?.GetValue(null) is float d && d > 0f) ? d : 3f);
			yield return (object)new WaitForSeconds(num2 + 1f);
			KillAll(bodies);
			foreach (KeyValuePair<Inventory, EquipmentIndex> item3 in previous)
			{
				if (Object.op_Implicit((Object)(object)item3.Key))
				{
					item3.Key.SetEquipmentIndex(item3.Value);
				}
			}
		}

		private static bool StartCountdown(CharacterBody body, Type countDownType)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			try
			{
				NetworkedBodyAttachment[] array = Object.FindObjectsOfType<NetworkedBodyAttachment>();
				foreach (NetworkedBodyAttachment val in array)
				{
					if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val.attachedBody != (Object)(object)body)
					{
						continue;
					}
					EntityStateMachine[] components = ((Component)val).GetComponents<EntityStateMachine>();
					foreach (EntityStateMachine val2 in components)
					{
						string text = ((object)val2.state)?.GetType().FullName;
						if (!(text != "EntityStates.QuestVolatileBattery.Monitor") || !(text != "EntityStates.QuestVolatileBattery.CountDown"))
						{
							if (text == "EntityStates.QuestVolatileBattery.Monitor")
							{
								val2.SetNextState((EntityState)Activator.CreateInstance(countDownType));
							}
							return true;
						}
					}
				}
			}
			catch (Exception ex)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)("Couldn't start the Fuel Array countdown directly. " + ex.Message));
			}
			return false;
		}

		private static void ForceBelowThreshold(CharacterBody body)
		{
			HealthComponent healthComponent = body.healthComponent;
			if (Object.op_Implicit((Object)(object)healthComponent) && healthComponent.alive)
			{
				healthComponent.Networkshield = 0f;
				healthComponent.Networkbarrier = 0f;
				healthComponent.Networkhealth = Mathf.Max(1f, healthComponent.fullHealth * 0.3f);
			}
		}

		private static void KillAll(IEnumerable<CharacterBody> bodies)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			foreach (CharacterBody body in bodies)
			{
				if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.healthComponent) && body.healthComponent.alive)
				{
					body.healthComponent.Suicide((GameObject)null, (GameObject)null, DamageTypeCombo.op_Implicit((DamageType)65536));
				}
			}
		}
	}
	internal static class MusicSilencer
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static PickTrackDelegate <0>__OnPickTrack;
		}

		public static bool Enabled = true;

		public static bool WagerWon;

		private const string WardStateNamespace = "EntityStates.Missions.Arena.NullWard";

		public static void Install()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			object obj = <>O.<0>__OnPickTrack;
			if (obj == null)
			{
				PickTrackDelegate val = OnPickTrack;
				<>O.<0>__OnPickTrack = val;
				obj = (object)val;
			}
			MusicController.pickTrackHook += (PickTrackDelegate)obj;
		}

		public static void Uninstall()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			object obj = <>O.<0>__OnPickTrack;
			if (obj == null)
			{
				PickTrackDelegate val = OnPickTrack;
				<>O.<0>__OnPickTrack = val;
				obj = (object)val;
			}
			MusicController.pickTrackHook -= (PickTrackDelegate)obj;
		}

		private static void OnPickTrack(MusicController controller, ref MusicTrackDef newTrack)
		{
			try
			{
				if (Enabled && ShouldBeSilent())
				{
					newTrack = null;
				}
			}
			catch (Exception ex)
			{
				Enabled = false;
				VoidCoinFlipPlugin.Log.LogWarning((object)("Music silencing turned off after an error. " + ex.Message));
			}
		}

		private static bool ShouldBeSilent()
		{
			ArenaMissionController instance = ArenaMissionController.instance;
			if (!Object.op_Implicit((Object)(object)instance) || instance.nullWards == null)
			{
				return false;
			}
			if (WagerWon)
			{
				return true;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			GameObject[] nullWards = instance.nullWards;
			foreach (GameObject val in nullWards)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					num3++;
					string text = WardState(val);
					if (text == "Active")
					{
						num++;
					}
					else if (text == "Complete")
					{
						num++;
						num2++;
					}
				}
			}
			if (!NetworkServer.active && num3 > 0 && num2 == num3)
			{
				return true;
			}
			return num == 0;
		}

		internal static string WardState(GameObject ward)
		{
			EntityStateMachine[] components = ward.GetComponents<EntityStateMachine>();
			for (int i = 0; i < components.Length; i++)
			{
				EntityState state = components[i].state;
				if (state != null && ((object)state).GetType().Namespace == "EntityStates.Missions.Arena.NullWard")
				{
					return ((object)state).GetType().Name;
				}
			}
			return null;
		}
	}
	internal class VoiceLine
	{
		public string Name;

		public float[] Samples;

		public int SampleRate;

		public float Length
		{
			get
			{
				if (SampleRate <= 0)
				{
					return 0f;
				}
				return (float)Samples.Length / (float)SampleRate;
			}
		}
	}
	internal static class ShrineVoice
	{
		public static readonly List<VoiceLine> Lines = new List<VoiceLine>();

		private const uint SND_ASYNC = 1u;

		private const uint SND_NODEFAULT = 2u;

		private const uint SND_MEMORY = 4u;

		private static GCHandle playing;

		private static bool unavailable;

		[DllImport("winmm.dll", SetLastError = true)]
		private static extern bool PlaySound(IntPtr sound, IntPtr module, uint flags);

		public static void LoadFrom(string folder)
		{
			Lines.Clear();
			foreach (string item in from p in Directory.GetFiles(folder, "shrine_voice*")
				orderby p
				select p)
			{
				if (Path.GetExtension(item).ToLowerInvariant() != ".wav")
				{
					VoidCoinFlipPlugin.Log.LogWarning((object)("Skipping " + Path.GetFileName(item) + ": only .wav files are supported for the shrine voice."));
					continue;
				}
				try
				{
					VoiceLine voiceLine = LoadWav(item);
					Lines.Add(voiceLine);
					VoidCoinFlipPlugin.Log.LogInfo((object)$"Loaded shrine voice {voiceLine.Name} ({voiceLine.Length:0.0}s).");
				}
				catch (Exception ex)
				{
					VoidCoinFlipPlugin.Log.LogWarning((object)("Couldn't read " + Path.GetFileName(item) + ": " + ex.Message));
				}
			}
			if (Lines.Count == 0)
			{
				VoidCoinFlipPlugin.Log.LogInfo((object)"No shrine_voice .wav files found; the shrine will stay silent.");
			}
			else
			{
				VoidCoinFlipPlugin.Log.LogInfo((object)$"Shrine has {Lines.Count} voice line(s).");
			}
		}

		private static VoiceLine LoadWav(string path)
		{
			byte[] array = File.ReadAllBytes(path);
			if (array.Length < 12 || Encoding.ASCII.GetString(array, 0, 4) != "RIFF" || Encoding.ASCII.GetString(array, 8, 4) != "WAVE")
			{
				throw new InvalidDataException("not a WAV file");
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = -1;
			int num6 = 0;
			int num7 = 12;
			while (num7 + 8 <= array.Length)
			{
				string text = Encoding.ASCII.GetString(array, num7, 4);
				int num8 = BitConverter.ToInt32(array, num7 + 4);
				int num9 = num7 + 8;
				if (text == "fmt ")
				{
					num = BitConverter.ToUInt16(array, num9);
					num2 = BitConverter.ToUInt16(array, num9 + 2);
					num3 = BitConverter.ToInt32(array, num9 + 4);
					num4 = BitConverter.ToUInt16(array, num9 + 14);
					if (num == 65534 && num8 >= 26)
					{
						num = BitConverter.ToUInt16(array, num9 + 24);
					}
				}
				else if (text == "data")
				{
					num5 = num9;
					num6 = ((num8 < 0 || num9 + num8 > array.Length) ? (array.Length - num9) : num8);
					break;
				}
				if (num8 < 0)
				{
					break;
				}
				num7 = num9 + num8 + (num8 & 1);
			}
			if (num5 < 0 || num2 <= 0 || num3 <= 0 || num4 <= 0)
			{
				throw new InvalidDataException("missing format or audio data");
			}
			if (num != 1 && num != 3)
			{
				throw new InvalidDataException($"unsupported WAV encoding ({num}); export as Linear PCM");
			}
			int num10 = num4 / 8;
			int num11 = num6 / (num10 * num2);
			float[] array2 = new float[num11];
			for (int i = 0; i < num11; i++)
			{
				float num12 = 0f;
				for (int j = 0; j < num2; j++)
				{
					int i2 = num5 + (i * num2 + j) * num10;
					num12 += ReadSample(array, i2, num4, num);
				}
				array2[i] = num12 / (float)num2;
			}
			return new VoiceLine
			{
				Name = Path.GetFileNameWithoutExtension(path),
				Samples = array2,
				SampleRate = num3
			};
		}

		private static float ReadSample(byte[] b, int i, int bits, int format)
		{
			if (format == 3 && bits == 32)
			{
				return BitConverter.ToSingle(b, i);
			}
			if (format == 3 && bits == 64)
			{
				return (float)BitConverter.ToDouble(b, i);
			}
			return bits switch
			{
				8 => (float)(b[i] - 128) / 128f, 
				16 => (float)BitConverter.ToInt16(b, i) / 32768f, 
				24 => (float)(b[i] | (b[i + 1] << 8) | ((sbyte)b[i + 2] << 16)) / 8388608f, 
				32 => (float)BitConverter.ToInt32(b, i) / 2.1474836E+09f, 
				_ => throw new InvalidDataException($"unsupported bit depth {bits}"), 
			};
		}

		public static void Play(VoiceLine line, float volume, float pan)
		{
			if (unavailable)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)"Shrine voice: Windows audio was unavailable earlier, not playing.");
			}
			else
			{
				if (line == null || line.Samples.Length == 0)
				{
					return;
				}
				float leftGain = volume * Math.Min(1f, 1f - pan);
				float rightGain = volume * Math.Min(1f, 1f + pan);
				byte[] value = BuildStereoWav(line, leftGain, rightGain);
				try
				{
					Stop();
					playing = GCHandle.Alloc(value, GCHandleType.Pinned);
					if (PlaySound(playing.AddrOfPinnedObject(), IntPtr.Zero, 7u))
					{
						VoidCoinFlipPlugin.Log.LogInfo((object)$"Shrine voice: playing {line.Name} (volume {volume:0.00}, pan {pan:0.00}).");
					}
					else
					{
						VoidCoinFlipPlugin.Log.LogWarning((object)$"Shrine voice: Windows refused to play {line.Name} (error {Marshal.GetLastWin32Error()}).");
					}
				}
				catch (Exception ex)
				{
					unavailable = true;
					VoidCoinFlipPlugin.Log.LogWarning((object)("Windows audio isn't available; the shrine will stay silent. " + ex.Message));
				}
			}
		}

		public static void Stop()
		{
			if (!unavailable)
			{
				try
				{
					PlaySound(IntPtr.Zero, IntPtr.Zero, 0u);
				}
				catch
				{
				}
				if (playing.IsAllocated)
				{
					playing.Free();
				}
			}
		}

		private static byte[] BuildStereoWav(VoiceLine line, float leftGain, float rightGain)
		{
			int num = line.Samples.Length;
			int num2 = num * 4;
			byte[] wav = new byte[44 + num2];
			Str(0, "RIFF");
			I(4, 36 + num2);
			Str(8, "WAVE");
			Str(12, "fmt ");
			I(16, 16);
			I16(20, 1);
			I16(22, 2);
			I(24, line.SampleRate);
			I(28, line.SampleRate * 4);
			I16(32, 4);
			I16(34, 16);
			Str(36, "data");
			I(40, num2);
			int num3 = 44;
			for (int i = 0; i < num; i++)
			{
				float num4 = line.Samples[i];
				short num5 = (short)(Math.Max(-1f, Math.Min(1f, num4 * leftGain)) * 32767f);
				short num6 = (short)(Math.Max(-1f, Math.Min(1f, num4 * rightGain)) * 32767f);
				wav[num3++] = (byte)num5;
				wav[num3++] = (byte)(num5 >> 8);
				wav[num3++] = (byte)num6;
				wav[num3++] = (byte)(num6 >> 8);
			}
			return wav;
			void I(int at, int v)
			{
				BitConverter.GetBytes(v).CopyTo(wav, at);
			}
			void I16(int at, short v)
			{
				BitConverter.GetBytes(v).CopyTo(wav, at);
			}
			void Str(int at, string s)
			{
				Encoding.ASCII.GetBytes(s, 0, 4, wav, at);
			}
		}
	}
	[BepInPlugin("com.deadLeshy.VoidCoinFlip", "VoidCoinFlip", "1.0.1")]
	public class VoidCoinFlipPlugin : BaseUnityPlugin
	{
		private enum Rig
		{
			None,
			Win,
			Loss
		}

		internal enum Call
		{
			None,
			Heads,
			Tails
		}

		public enum LossOutcome
		{
			Die,
			SkipVoidFields
		}

		public const string GUID = "com.deadLeshy.VoidCoinFlip";

		public const string PluginName = "VoidCoinFlip";

		public const string Version = "1.0.1";

		internal static ManualLogSource Log;

		internal static VoidCoinFlipPlugin Instance;

		private ConfigEntry<string> winRewards;

		private ConfigEntry<int> optionsPerPick;

		private ConfigEntry<bool> goldBonus;

		private ConfigEntry<int> goldChestsWorth;

		private ConfigEntry<float> callTimeout;

		private ConfigEntry<KeyCode> headsKey;

		private ConfigEntry<KeyCode> tailsKey;

		private ConfigEntry<KeyCode> voiceTestKey;

		private ConfigEntry<bool> debugKeys;

		private ConfigEntry<KeyCode> forceWinKey;

		private ConfigEntry<KeyCode> forceLossKey;

		private ConfigEntry<KeyCode> jumpToVoidKey;

		private ConfigEntry<KeyCode> chimeTestKey;

		private ConfigEntry<bool> winChimeEnabled;

		private Rig rigNextFlip;

		private ConfigEntry<LossOutcome> lossOutcome;

		private bool awaitingCall;

		private bool walkedAway;

		private Call pendingCall;

		private NetworkUser caller;

		private PurchaseInteraction shrine;

		private SimpleDialogBox dialog;

		private Coroutine flow;

		private bool wagerUsed;

		private bool exitMode;

		private static readonly Random rng = new Random();

		private const string ShrineCardKey = "RoR2/Base/ShrineChance/iscShrineChance.asset";

		private const string VoidFieldsScene = "arena";

		private Coroutine clientDecorate;

		private static GameObject optionPickupPrefab;

		private const float PauseAfterWinMessage = 1.5f;

		private const float PauseAfterRewardMessage = 1f;

		private const float PauseBetweenItems = 0.35f;

		internal bool IsDialogOpen
		{
			get
			{
				if (Object.op_Implicit((Object)(object)dialog))
				{
					return Object.op_Implicit((Object)(object)dialog.rootObject);
				}
				return false;
			}
		}

		private static GameObject OptionPickupPrefab
		{
			get
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				if (!Object.op_Implicit((Object)(object)optionPickupPrefab))
				{
					try
					{
						optionPickupPrefab = Addressables.LoadAssetAsync<GameObject>((object)"RoR2/DLC1/OptionPickup/OptionPickup.prefab").WaitForCompletion();
					}
					catch (Exception arg)
					{
						Log.LogWarning((object)$"Couldn't load the choice pickup; rewards will be single items. {arg}");
					}
				}
				return optionPickupPrefab;
			}
		}

		private void Awake()
		{
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			lossOutcome = ((BaseUnityPlugin)this).Config.Bind<LossOutcome>("General", "LossOutcome", LossOutcome.Die, "What happens on a wrong call. Die: everyone's Fuel Array detonates. SkipVoidFields: you're sent to the next stage with no rewards.");
			callTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CallTimeoutSeconds", 60f, "If no call is made this long after the shrine is used, the wager is cancelled and the shrine can be used again.");
			winRewards = ((BaseUnityPlugin)this).Config.Bind<string>("Rewards", "WinRewards", "Tier1,Tier1,Tier1,Tier1,Tier2,Tier2,Tier2,Tier2,Tier3", "Reward pickups each player gets for a correct call, one per cell (vanilla: 4 white, 4 green, 1 red). Options: Tier1, Tier2, Tier3, Boss, Lunar, Void. Leave empty for no items.");
			optionsPerPick = ((BaseUnityPlugin)this).Config.Bind<int>("Rewards", "OptionsPerPick", 3, "How many items to choose between in each reward pickup.");
			goldBonus = ((BaseUnityPlugin)this).Config.Bind<bool>("Rewards", "GoldBonus", true, "Also give every player a lump sum of gold on a correct call.");
			goldChestsWorth = ((BaseUnityPlugin)this).Config.Bind<int>("Rewards", "GoldChestsWorth", 5, "Size of the gold bonus, measured in small chests at the current difficulty.");
			WagerShrineVisuals.VoiceEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "ShrineVoice", true, "Let the shrine speak when you're near it. Needs your own shrine_voice*.wav files next to the mod's .dll.").Value;
			WagerShrineVisuals.VoiceVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "VoiceVolume", 1.5f, "Volume of the shrine's voice. 1 is the clip as recorded; above 1 boosts it (past ~2 may distort).").Value;
			WagerShrineVisuals.VoiceInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "SecondsBetweenLines", 20f, "Pause between the shrine's lines while you're near it.").Value;
			WagerShrineVisuals.VoiceRange = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "HearingRange", 18f, "How close (in meters) you need to be to hear the shrine.").Value;
			winChimeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "WinChime", true, "Play the Void Fields cell-complete sound when the coin flip is won.");
			MusicSilencer.Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "QuietVoidFields", true, "No music in the Void Fields until someone activates a cell vent, and none after a winning flip, so the shrine can be heard.").Value;
			headsKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "HeadsKey", (KeyCode)104, "Key for the host to call heads (in case the popup doesn't take mouse input).");
			tailsKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "TailsKey", (KeyCode)116, "Key for the host to call tails.");
			debugKeys = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugKeys", false, "Turn on the testing hotkeys below (host only). Leave off for normal play.");
			jumpToVoidKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "JumpToVoidFieldsKey", (KeyCode)288, "Load the Void Fields right now during a run. Press again inside it for a fresh one.");
			forceWinKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "ForceWinKey", (KeyCode)291, "Make the next coin flip a guaranteed win. Press again to cancel.");
			forceLossKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "ForceLossKey", (KeyCode)292, "Make the next coin flip a guaranteed loss. Press again to cancel.");
			voiceTestKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "VoiceTestKey", (KeyCode)290, "Play a random shrine voice line right away (works at the main menu).");
			chimeTestKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "WinChimeTestKey", (KeyCode)289, "Play the win chime right away (works at the main menu).");
			MusicSilencer.Install();
			Chat.onChatChanged += OnChatChangedForChime;
			ShrineVoice.LoadFrom(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
			Stage.onServerStageBegin += OnServerStageBegin;
			Stage.onStageStartGlobal += OnStageStartGlobal;
			Harmony val = new Harmony("com.deadLeshy.VoidCoinFlip");
			val.PatchAll(typeof(ChatCallPatch));
			val.PatchAll(typeof(ShrineActivatePatch));
			val.PatchAll(typeof(HideVentPromptPatch));
			try
			{
				val.PatchAll(typeof(EscapeClosesCoinPopupPatch));
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Couldn't hook Escape for the coin popup; use the Walk away button. " + ex.Message));
			}
			Log.LogInfo((object)"VoidCoinFlip 1.0.1 loaded.");
		}

		private void OnDestroy()
		{
			MusicSilencer.Uninstall();
			Chat.onChatChanged -= OnChatChangedForChime;
			Stage.onServerStageBegin -= OnServerStageBegin;
			Stage.onStageStartGlobal -= OnStageStartGlobal;
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private bool InTriggerScene()
		{
			SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene();
			return ((sceneDefForCurrentScene != null) ? sceneDefForCurrentScene.baseSceneName : null) == "arena";
		}

		private void OnStageStartGlobal(Stage stage)
		{
			if (clientDecorate != null)
			{
				((MonoBehaviour)this).StopCoroutine(clientDecorate);
				clientDecorate = null;
			}
			if (!NetworkServer.active && InTriggerScene())
			{
				clientDecorate = ((MonoBehaviour)this).StartCoroutine(DecorateShrineAsClient());
			}
		}

		private IEnumerator DecorateShrineAsClient()
		{
			for (float t = 0f; t < 120f; t += 1f)
			{
				if (!InTriggerScene())
				{
					yield break;
				}
				PurchaseInteraction[] array = Object.FindObjectsOfType<PurchaseInteraction>();
				foreach (PurchaseInteraction val in array)
				{
					if (Object.op_Implicit((Object)(object)val) && ((Object)((Component)val).gameObject).name.StartsWith("ShrineChance") && !Object.op_Implicit((Object)(object)((Component)val).GetComponent<WagerShrineVisuals>()))
					{
						val.displayNameToken = "Shrine of the Void's Wager";
						val.contextToken = "Call the coin";
						WagerShrineVisuals.Apply(((Component)val).gameObject);
						clientDecorate = null;
						yield break;
					}
				}
				yield return (object)new WaitForSeconds(1f);
			}
			clientDecorate = null;
		}

		private void OnServerStageBegin(Stage stage)
		{
			CloseDialog();
			awaitingCall = false;
			wagerUsed = false;
			exitMode = false;
			MusicSilencer.WagerWon = false;
			shrine = null;
			caller = null;
			if (flow != null)
			{
				((MonoBehaviour)this).StopCoroutine(flow);
			}
			if (InTriggerScene())
			{
				Log.LogInfo((object)"Entered the Void Fields, spawning the wager shrine.");
				((MonoBehaviour)this).StartCoroutine(SpawnShrineWhenReady());
			}
		}

		private IEnumerator SpawnShrineWhenReady()
		{
			float timeout = 30f;
			while (timeout > 0f && !AllPlayerBodies().Any())
			{
				timeout -= Time.deltaTime;
				yield return null;
			}
			yield return (object)new WaitForSeconds(1f);
			CharacterBody anchor = AllPlayerBodies().FirstOrDefault();
			if (!Object.op_Implicit((Object)(object)anchor))
			{
				Log.LogWarning((object)"No players found; not spawning the shrine.");
				yield break;
			}
			GameObject firstVent = null;
			for (float t = 0f; t < 10f; t += 0.5f)
			{
				GameObject val;
				firstVent = (val = FindReadyVent());
				if (Object.op_Implicit((Object)(object)val))
				{
					break;
				}
				yield return (object)new WaitForSeconds(0.5f);
			}
			GameObject val2 = (Object.op_Implicit((Object)(object)firstVent) ? SpawnShrineInVent(firstVent) : SpawnShrineNear(anchor.footPosition, 6f, 20f));
			if (!Object.op_Implicit((Object)(object)firstVent))
			{
				Log.LogWarning((object)"Couldn't find the first cell vent; spawning the shrine near the players instead.");
			}
			if (!Object.op_Implicit((Object)(object)val2))
			{
				Log.LogError((object)"Couldn't spawn the wager shrine.");
				yield break;
			}
			shrine = val2.GetComponent<PurchaseInteraction>();
			ConvertToWagerShrine(val2);
			Broadcast("<color=#c08cff>A <b>Shrine of the Void's Wager</b> has appeared by the first cell vent. Gamble on a coin flip, or activate the vent to face the trials. You can't do both.</color>");
			((MonoBehaviour)this).StartCoroutine(WithdrawShrineIfVentStarted());
		}

		private static GameObject FindReadyVent()
		{
			ArenaMissionController instance = ArenaMissionController.instance;
			if (!Object.op_Implicit((Object)(object)instance) || instance.nullWards == null)
			{
				return null;
			}
			return ((IEnumerable<GameObject>)instance.nullWards).FirstOrDefault((Func<GameObject, bool>)((GameObject w) => Object.op_Implicit((Object)(object)w) && MusicSilencer.WardState(w) == "WardOnAndReady"));
		}

		private static float VentSafeRadius(GameObject vent)
		{
			SphereZone componentInChildren = vent.GetComponentInChildren<SphereZone>();
			if (Object.op_Implicit((Object)(object)componentInChildren) && (AccessTools.Property(typeof(SphereZone), "radius")?.GetValue(componentInChildren) ?? AccessTools.Field(typeof(SphereZone), "radius")?.GetValue(componentInChildren)) is float num && num > 1f)
			{
				return num;
			}
			return 15f;
		}

		private GameObject SpawnShrineInVent(GameObject vent)
		{
			//IL_000d: 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_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: 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_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			float num = VentSafeRadius(vent);
			Vector3 position = vent.transform.position;
			List<CharacterBody> list = AllPlayerBodies().ToList();
			Vector3 val = Vector3.forward;
			if (list.Count > 0)
			{
				Vector3 val2 = list.Aggregate(Vector3.zero, (Vector3 acc, CharacterBody b) => acc + b.footPosition) / (float)list.Count - position;
				val2.y = 0f;
				if (((Vector3)(ref val2)).sqrMagnitude > 0.25f)
				{
					val = ((Vector3)(ref val2)).normalized;
				}
			}
			RaycastHit val4 = default(RaycastHit);
			for (int num2 = 0; num2 < 12; num2++)
			{
				float num3 = (float)(((num2 % 2 == 0) ? 1 : (-1)) * ((num2 + 1) / 2)) * 30f;
				Vector3 val3 = Quaternion.Euler(0f, num3, 0f) * val;
				if (Physics.Raycast(position + val3 * num + Vector3.up * 15f, Vector3.down, ref val4, 40f, LayerMask.op_Implicit(((LayerIndex)(ref LayerIndex.world)).mask)))
				{
					Quaternion rotation = Quaternion.LookRotation(val3) * Quaternion.Euler(0f, 180f, 0f);
					Log.LogInfo((object)$"Placing the shrine on the edge of {((Object)vent).name}'s safe zone (radius {num:0.0}m).");
					return SpawnShrineAt(((RaycastHit)(ref val4)).point, rotation);
				}
			}
			Log.LogWarning((object)"No ground at the vent's edge; using a nearby spot instead.");
			return SpawnShrineNear(position, num * 0.8f, num * 1.2f);
		}

		private GameObject SpawnShrineAt(Vector3 position, Quaternion rotation)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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)
			try
			{
				InteractableSpawnCard val = Addressables.LoadAssetAsync<InteractableSpawnCard>((object)"RoR2/Base/ShrineChance/iscShrineChance.asset").WaitForCompletion();
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)((SpawnCard)val).prefab))
				{
					return null;
				}
				GameObject obj = Object.Instantiate<GameObject>(((SpawnCard)val).prefab, position, rotation);
				NetworkServer.Spawn(obj);
				return obj;
			}
			catch (Exception ex)
			{
				Log.LogError((object)ex);
				return null;
			}
		}

		private GameObject SpawnShrineNear(Vector3 center, float minDistance, float maxDistance)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//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_003c: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: 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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				InteractableSpawnCard val = Addressables.LoadAssetAsync<InteractableSpawnCard>((object)"RoR2/Base/ShrineChance/iscShrineChance.asset").WaitForCompletion();
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)DirectorCore.instance))
				{
					return null;
				}
				DirectorPlacementRule val2 = new DirectorPlacementRule
				{
					placementMode = (PlacementMode)1,
					position = center,
					minDistance = minDistance,
					maxDistance = maxDistance
				};
				DirectorSpawnRequest val3 = new DirectorSpawnRequest((SpawnCard)(object)val, val2, Run.instance.stageRng);
				GameObject val4 = DirectorCore.instance.TrySpawnObject(val3);
				if (Object.op_Implicit((Object)(object)val4))
				{
					return val4;
				}
				Log.LogWarning((object)"Placement failed; finding ground for the shrine manually.");
				float num = (minDistance + maxDistance) * 0.5f;
				Vector3 val5 = center + Vector3.forward * num;
				RaycastHit val6 = default(RaycastHit);
				for (int i = 0; i < 8; i++)
				{
					if (Physics.Raycast(center + Quaternion.Euler(0f, (float)i * 45f, 0f) * Vector3.forward * num + Vector3.up * 15f, Vector3.down, ref val6, 40f, LayerMask.op_Implicit(((LayerIndex)(ref LayerIndex.world)).mask)))
					{
						val5 = ((RaycastHit)(ref val6)).point;
						break;
					}
				}
				GameObject obj = Object.Instantiate<GameObject>(((SpawnCard)val).prefab, val5, Quaternion.identity);
				NetworkServer.Spawn(obj);
				return obj;
			}
			catch (Exception ex)
			{
				Log.LogError((object)ex);
				return null;
			}
		}

		private IEnumerator WithdrawShrineIfVentStarted()
		{
			while (Object.op_Implicit((Object)(object)shrine) && !wagerUsed)
			{
				ArenaMissionController instance = ArenaMissionController.instance;
				if (Object.op_Implicit((Object)(object)instance) && instance.nullWards != null && instance.nullWards.Any((GameObject w) => Object.op_Implicit((Object)(object)w) && MusicSilencer.WardState(w) == "Active"))
				{
					if (awaitingCall)
					{
						if (flow != null)
						{
							((MonoBehaviour)this).StopCoroutine(flow);
						}
						awaitingCall = false;
						caller = null;
						CloseDialog();
					}
					Broadcast("<color=#c08cff>A cell vent has been activated. The void's wager is withdrawn.</color>");
					Log.LogInfo((object)"Cell vent started normally; removing the wager shrine.");
					NetworkServer.Destroy(((Component)shrine).gameObject);
					shrine = null;
					break;
				}
				yield return (object)new WaitForSeconds(0.5f);
			}
		}

		private void ConvertToWagerShrine(GameObject shrineObject)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			ShrineChanceBehavior component = shrineObject.GetComponent<ShrineChanceBehavior>();
			if (Object.op_Implicit((Object)(object)component))
			{
				Object.Destroy((Object)(object)component);
			}
			if (Object.op_Implicit((Object)(object)shrine))
			{
				shrine.displayNameToken = "Shrine of the Void's Wager";
				shrine.contextToken = "Call the coin";
				WagerShrineVisuals.Apply(shrineObject);
				shrine.costType = (CostTypeIndex)0;
				shrine.Networkcost = 0;
			}
		}

		internal bool IsWagerShrine(PurchaseInteraction pi)
		{
			if (Object.op_Implicit((Object)(object)pi))
			{
				return (Object)(object)pi == (Object)(object)shrine;
			}
			return false;
		}

		internal void OnShrineActivated(Interactor interactor)
		{
			if (exitMode)
			{
				exitMode = false;
				if (Object.op_Implicit((Object)(object)shrine))
				{
					shrine.SetAvailable(false);
				}
				Broadcast("<color=#c08cff>Leaving the void...</color>");
				((MonoBehaviour)this).StartCoroutine(LeaveAfter(2f));
			}
			else
			{
				if (wagerUsed || awaitingCall)
				{
					return;
				}
				CharacterBody val = (Object.op_Implicit((Object)(object)interactor) ? ((Component)interactor).GetComponent<CharacterBody>() : null);
				NetworkUser val2 = ((Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.master) && Object.op_Implicit((Object)(object)val.master.playerCharacterMasterController)) ? val.master.playerCharacterMasterController.networkUser : null);
				if (Object.op_Implicit((Object)(object)val2))
				{
					shrine.SetAvailable(false);
					if (flow != null)
					{
						((MonoBehaviour)this).StopCoroutine(flow);
					}
					flow = ((MonoBehaviour)this).StartCoroutine(CoinFlipFlow(val2));
				}
			}
		}

		private IEnumerator CoinFlipFlow(NetworkUser user)
		{
			caller = user;
			pendingCall = Call.None;
			walkedAway = false;
			awaitingCall = true;
			bool isLocalPlayer = ((NetworkBehaviour)user).isLocalPlayer;
			string name = user.userName;
			if (isLocalPlayer)
			{
				Broadcast("<color=#c08cff><b>" + name + "</b> accepts the void's wager and is calling the coin...</color>");
				OpenDialog();
			}
			else
			{
				Broadcast("<color=#c08cff><b>" + name + "</b> accepts the void's wager. <b>" + name + "</b>: type <b>heads</b> or <b>tails</b> in chat.</color>");
			}
			float waited = 0f;
			while (pendingCall == Call.None && !walkedAway && waited < callTimeout.Value)
			{
				if (!NetworkServer.active)
				{
					yield break;
				}
				waited += Time.deltaTime;
				yield return null;
			}
			awaitingCall = false;
			CloseDialog();
			if (pendingCall == Call.None)
			{
				Broadcast(walkedAway ? ("<color=#c08cff><b>" + name + "</b> walks away from the wager. The shrine waits for another taker.</color>") : "<color=#c08cff>No call was made. The shrine waits for another taker.</color>");
				caller = null;
				if (Object.op_Implicit((Object)(object)shrine))
				{
					shrine.SetAvailable(true);
				}
				yield break;
			}
			wagerUsed = true;
			Call result = ((rng.Next(2) == 0) ? Call.Heads : Call.Tails);
			if (rigNextFlip != Rig.None)
			{
				bool flag = rigNextFlip == Rig.Win;
				result = (flag ? pendingCall : ((pendingCall != Call.Heads) ? Call.Heads : Call.Tails));
				Log.LogInfo((object)("[Debug] Forced " + (flag ? "win" : "loss") + " used."));
				rigNextFlip = Rig.None;
			}
			Broadcast($"<color=#c08cff><b>{name}</b> calls <b>{pendingCall}</b>. The coin spins...</color>");
			yield return (object)new WaitForSeconds(1.5f);
			if (result == pendingCall)
			{
				Broadcast(string.Format("<color=#7fff7f>It lands <b>{0}</b>! {1}.</color>", result, "The void trials are passed"));
				if (winChimeEnabled.Value)
				{
					WinChime.Play();
				}
				MusicSilencer.WagerWon = true;
				bool completed = VoidFieldsCompletion.Finish();
				((MonoBehaviour)this).StartCoroutine(RelockVents());
				((MonoBehaviour)this).StartCoroutine(RemoveVentShortly());
				yield return (object)new WaitForSeconds(1.5f);
				yield return DropRewards();
				yield return (object)new WaitForSeconds(1f);
				ApplyWinBonus();
				yield return (object)new WaitForSeconds(2f);
				if (completed)
				{
					Broadcast("<color=#c08cff>The void is beaten. Take a portal when you're ready.</color>");
					yield break;
				}
				exitMode = true;
				if (Object.op_Implicit((Object)(object)shrine))
				{
					shrine.contextToken = "Leave the void";
					shrine.SetAvailable(true);
				}
				Broadcast("<color=#c08cff>The fog lifts. Use the shrine again when you're ready to leave.</color>");
			}
			else if (lossOutcome.Value == LossOutcome.SkipVoidFields)
			{
				Broadcast($"<color=#ff5050>It lands <b>{result}</b>. The void casts you out empty-handed.</color>");
				yield return (object)new WaitForSeconds(3f);
				AdvanceToNextStage();
			}
			else
			{
				Broadcast($"<color=#ff5050>It lands <b>{result}</b>.</color>");
				Broadcast("<color=#ff5050>You have lost the void's wager. Your soul is now forfeit.</color>");
				yield return (object)new WaitForSeconds(0.5f);
				yield return FuelArrayDeath.Run(AllPlayerBodies().ToList());
			}
		}

		internal void OnChatMessage(NetworkUser sender, string text)
		{
			if (awaitingCall && Object.op_Implicit((Object)(object)sender) && !((Object)(object)sender != (Object)(object)caller) && text != null)
			{
				switch (text.Trim().ToLowerInvariant())
				{
				case "heads":
				case "h":
					pendingCall = Call.Heads;
					break;
				case "tails":
				case "t":
					pendingCall = Call.Tails;
					break;
				}
			}
		}

		private void ToggleRig(Rig rig)
		{
			rigNextFlip = ((rigNextFlip != rig) ? rig : Rig.None);
			Log.LogInfo((object)((rigNextFlip == Rig.None) ? "[Debug] Forced outcome cancelled; the next flip is random." : ("[Debug] Next coin flip will be a " + ((rigNextFlip == Rig.Win) ? "win" : "loss") + ".")));
		}

		private void JumpToVoidFields()
		{
			Run instance = Run.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				Log.LogInfo((object)"[Debug] Start a run first, then press the jump key.");
				return;
			}
			SceneDef val = SceneCatalog.FindSceneDef("arena");
			if (!Object.op_Implicit((Object)(object)val))
			{
				Log.LogWarning((object)"[Debug] Couldn't find the Void Fields scene.");
				return;
			}
			Log.LogInfo((object)"[Debug] Jumping to the Void Fields.");
			instance.AdvanceStage(val);
		}

		private void OnChatChangedForChime()
		{
			if (winChimeEnabled.Value)
			{
				WinChime.OnChatChanged();
			}
		}

		private void Update()
		{
			//IL_0013: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_0166: 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)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			if (debugKeys.Value && (int)chimeTestKey.Value != 0 && Input.GetKeyDown(chimeTestKey.Value))
			{
				Log.LogInfo((object)"Win chime: test key pressed.");
				WinChime.Play();
			}
			if (debugKeys.Value && NetworkServer.active)
			{
				if ((int)forceWinKey.Value != 0 && Input.GetKeyDown(forceWinKey.Value))
				{
					ToggleRig(Rig.Win);
				}
				if ((int)forceLossKey.Value != 0 && Input.GetKeyDown(forceLossKey.Value))
				{
					ToggleRig(Rig.Loss);
				}
				if ((int)jumpToVoidKey.Value != 0 && Input.GetKeyDown(jumpToVoidKey.Value))
				{
					JumpToVoidFields();
				}
			}
			if (debugKeys.Value && (int)voiceTestKey.Value != 0 && Input.GetKeyDown(voiceTestKey.Value) && WagerShrineVisuals.VoiceEnabled && ShrineVoice.Lines.Count > 0)
			{
				Log.LogInfo((object)"Shrine voice: test key pressed.");
				ShrineVoice.Play(ShrineVoice.Lines[Random.Range(0, ShrineVoice.Lines.Count)], WagerShrineVisuals.VoiceVolume, 0f);
			}
			if (awaitingCall && Object.op_Implicit((Object)(object)caller) && ((NetworkBehaviour)caller).isLocalPlayer)
			{
				if (Input.GetKeyDown(headsKey.Value))
				{
					pendingCall = Call.Heads;
				}
				else if (Input.GetKeyDown(tailsKey.Value))
				{
					pendingCall = Call.Tails;
				}
			}
		}

		private void OpenDialog()
		{
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			try
			{
				LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
				dialog = SimpleDialogBox.Create((firstLocalUser != null) ? firstLocalUser.eventSystem : null);
				dialog.headerToken = new TokenParamsPair("THE VOID'S WAGER", Array.Empty<object>());
				dialog.descriptionToken = new TokenParamsPair("Call the coin.\n\nRight: skip the void trials and claim their rewards.\n" + ((lossOutcome.Value == LossOutcome.Die) ? "Wrong: everyone dies.\n\n" : "Wrong: leave the Void Fields with nothing.\n\n") + $"(Or press {headsKey.Value} for heads, {tailsKey.Value} for tails. Escape to walk away.)", Array.Empty<object>());
				dialog.AddActionButton((UnityAction)delegate
				{
					pendingCall = Call.Heads;
				}, "Heads", true, Array.Empty<object>());
				dialog.AddActionButton((UnityAction)delegate
				{
					pendingCall = Call.Tails;
				}, "Tails", true, Array.Empty<object>());
				dialog.AddActionButton((UnityAction)delegate
				{
					walkedAway = true;
				}, "Walk away", true, Array.Empty<object>());
				if (Object.op_Implicit((Object)(object)dialog.rootObject) && !Object.op_Implicit((Object)(object)dialog.rootObject.GetComponent<CursorOpener>()))
				{
					dialog.rootObject.AddComponent<CursorOpener>();
				}
			}
			catch (Exception arg)
			{
				Log.LogWarning((object)$"Couldn't open the popup, use the keys instead. {arg}");
				Broadcast($"<color=#c08cff>Press <b>{headsKey.Value}</b> for heads or <b>{tailsKey.Value}</b> for tails, or type it in chat.</color>");
			}
		}

		internal void WalkAwayFromDialog()
		{
			if (awaitingCall)
			{
				walkedAway = true;
				CloseDialog();
			}
		}

		private void CloseDialog()
		{
			if (Object.op_Implicit((Object)(object)dialog) && Object.op_Implicit((Object)(object)dialog.rootObject))
			{
				Object.Destroy((Object)(object)dialog.rootObject);
			}
			dialog = null;
		}

		private static IEnumerable<CharacterBody> AllPlayerBodies()
		{
			return from p in PlayerCharacterMasterController.instances
				where Object.op_Implicit((Object)(object)p) && Object.op_Implicit((Object)(object)p.master)
				select p.master.GetBody() into b
				where Object.op_Implicit((Object)(object)b) && Object.op_Implicit((Object)(object)b.healthComponent) && b.healthComponent.alive
				select b;
		}

		private IEnumerator DropRewards()
		{
			Run run = Run.instance;
			if (!Object.op_Implicit((Object)(object)run))
			{
				yield break;
			}
			List<string> tiers = (from s in winRewards.Value.Split(',')
				select s.Trim() into s
				where s.Length > 0
				select s).ToList();
			if (tiers.Count == 0)
			{
				yield break;
			}
			GameObject prefab = OptionPickupPrefab;
			List<CharacterBody> bodies = AllPlayerBodies().ToList();
			Dictionary<CharacterBody, Vector3> piles = PilePositions(bodies);
			for (int p = 0; p < bodies.Count; p++)
			{
				CharacterBody body = bodies[p];
				if (!Object.op_Implicit((Object)(object)body))
				{
					continue;
				}
				string text = ((Object.op_Implicit((Object)(object)body.master) && Object.op_Implicit((Object)(object)body.master.playerCharacterMasterController) && Object.op_Implicit((Object)(object)body.master.playerCharacterMasterController.networkUser)) ? body.master.playerCharacterMasterController.networkUser.userName : body.GetDisplayName());
				Broadcast("<color=#c08cff>Here are <b>" + text + "</b>'s rewards</color>");
				yield return (object)new WaitForSeconds(1f);
				Vector3 val = piles[body];
				Vector3 origin = val + Vector3.up * 4f;
				for (int i = 0; i < tiers.Count; i++)
				{
					List<PickupIndex> list = DropListFor(run, tiers[i]);
					if (list != null && list.Count != 0)
					{
						PickupIndex[] array = list.OrderBy((PickupIndex _) => rng.Next()).Take(Math.Max(1, optionsPerPick.Value)).ToArray();
						Vector3 val2 = Quaternion.AngleAxis(360f / (float)tiers.Count * (float)i, Vector3.up) * (Vector3.forward * 2.5f) + Vector3.up * 6f;
						if (Object.op_Implicit((Object)(object)prefab))
						{
							CreatePickupInfo val3 = new CreatePickupInfo
							{
								pickerOptions = PickupPickerController.GenerateOptionsFromArray(array),
								prefabOverride = prefab,
								position = origin,
								rotation = Quaternion.identity
							};
							((CreatePickupInfo)(ref val3)).pickupIndex = PickupCatalog.FindPickupIndex(ItemTierFor(tiers[i]));
							PickupDropletController.CreatePickupDroplet(val3, origin, val2);
						}
						else
						{
							PickupDropletController.CreatePickupDroplet(array[0], origin, val2);
						}
						if (i < tiers.Count - 1)
						{
							yield return (object)new WaitForSeconds(0.35f);
						}
					}
				}
				if (p < bodies.Count - 1)
				{
					yield return (object)new WaitForSeconds(2f);
				}
			}
		}

		private Dictionary<CharacterBody, Vector3> PilePositions(List<CharacterBody> bodies)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//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_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: 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_014d: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<CharacterBody, Vector3> dictionary = new Dictionary<CharacterBody, Vector3>();
			if (bodies.Count == 0)
			{
				return dictionary;
			}
			Vector3 center = (Object.op_Implicit((Object)(object)shrine) ? ((Component)shrine).transform.position : bodies[0].footPosition);
			List<(CharacterBody, float)> list = (from x in bodies.Select(delegate(CharacterBody b)
				{
					//IL_0001: Unknown result type (might be due to invalid IL or missing references)
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0011: 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_0032: Unknown result type (might be due to invalid IL or missing references)
					Vector3 val2 = b.footPosition - center;
					val2.y = 0f;
					float num3 = ((((Vector3)(ref val2)).sqrMagnitude < 0.01f) ? 0f : (Mathf.Atan2(val2.x, val2.z) * 57.29578f));
					return (body: b, angle: (num3 + 360f) % 360f);
				})
				orderby x.angle
				select x).ToList();
			float num = Mathf.Min(70f, 360f / (float)bodies.Count);
			for (int num2 = 1; num2 < list.Count; num2++)
			{
				if (list[num2].Item2 - list[num2 - 1].Item2 < num)
				{
					list[num2] = (list[num2].Item1, list[num2 - 1].Item2 + num);
				}
			}
			foreach (var item3 in list)
			{
				CharacterBody item = item3.Item1;
				float item2 = item3.Item2;
				Vector3 val = Quaternion.Euler(0f, item2, 0f) * Vector3.forward;
				dictionary[item] = center + val * 6f;
			}
			return dictionary;
		}

		private static ItemTier ItemTierFor(string tier)
		{
			switch (tier.ToLowerInvariant())
			{
			case "tier2":
			case "green":
				return (ItemTier)1;
			case "tier3":
			case "red":
				return (ItemTier)2;
			case "boss":
			case "yellow":
				return (ItemTier)4;
			case "lunar":
			case "blue":
				return (ItemTier)3;
			case "void":
			case "purple":
				return (ItemTier)6;
			default:
				return (ItemTier)0;
			}
		}

		private static List<PickupIndex> DropListFor(Run run, string tier)
		{
			switch (tier.ToLowerInvariant())
			{
			case "tier1":
			case "white":
				return run.availableTier1DropList;
			case "tier2":
			case "green":
				return run.availableTier2DropList;
			case "tier3":
			case "red":
				return run.availableTier3DropList;
			case "boss":
			case "yellow":
				return run.availableBossDropList;
			case "lunar":
			case "blue":
				return run.availableLunarItemDropList;
			case "void":
			case "purple":
				return run.availableVoidTier1DropList.Concat(run.availableVoidTier2DropList).Concat(run.availableVoidTier3DropList).ToList();
			default:
				Log.LogWarning((object)("Unknown reward tier '" + tier + "', skipping."));
				return null;
			}
		}

		private IEnumerator RelockVents()
		{
			while (MusicSilencer.WagerWon && Object.op_Implicit((Object)(object)ArenaMissionController.instance))
			{
				VoidFieldsCompletion.LockVents();
				yield return (object)new WaitForSeconds(0.5f);
			}
		}

		private IEnumerator RemoveVentShortly()
		{
			yield return (object)new WaitForSeconds(1f);
			VoidFieldsCompletion.RemoveReadyVent();
		}

		private IEnumerator LeaveAfter(float seconds)
		{
			yield return (object)new WaitForSeconds(seconds);
			AdvanceToNextStage();
		}

		private void ApplyWinBonus()
		{
			Run instance = Run.instance;
			if (!Object.op_Implicit((Object)(object)instance) || !goldBonus.Value)
			{
				return;
			}
			int difficultyScaledCost = instance.GetDifficultyScaledCost(25);
			uint num = (uint)Math.Max(0, difficultyScaledCost * goldChestsWorth.Value);
			if (num == 0)
			{
				return;
			}
			foreach (PlayerCharacterMasterController item in PlayerCharacterMasterController.instances.Where((PlayerCharacterMasterController p) => Object.op_Implicit((Object)(object)p) && Object.op_Implicit((Object)(object)p.master)))
			{
				item.master.GiveMoney(num);
			}
			Broadcast($"<color=#ffd34d>The void pays out <b>${num}</b> to each survivor.</color>");
		}

		private static void ConvertGoldToExperience()
		{
			ulong num = 0uL;
			foreach (PlayerCharacterMasterController item in PlayerCharacterMasterController.instances.Where((PlayerCharacterMasterController p) => Object.op_Implicit((Object)(object)p) && Object.op_Implicit((Object)(object)p.master)))
			{
				uint money = item.master.money;
				if (money != 0)
				{
					num += money;
					item.master.money = 0u;
				}
			}
			if (num != 0L)
			{
				if (Object.op_Implicit((Object)(object)TeamManager.instance))
				{
					TeamManager.instance.GiveTeamExperience((TeamIndex)1, num);
					Log.LogInfo((object)$"Converted ${num} of unspent gold into experience.");
				}
				else
				{
					Log.LogWarning((object)"Couldn't find the team manager; unspent gold wasn't converted.");
				}
			}
		}

		private void AdvanceToNextStage()
		{
			Run instance = Run.instance;
			if (Object.op_Implicit((Object)(object)instance))
			{
				ConvertGoldToExperience();
				if (!Object.op_Implicit((Object)(object)instance.nextStageScene))
				{
					Log.LogWarning((object)"No next stage queued; letting the game pick one.");
					instance.PickNextStageSceneFromCurrentSceneDestinations();
				}
				if (Object.op_Implicit((Object)(object)instance.nextStageScene))
				{
					instance.AdvanceStage(instance.nextStageScene);
				}
				else
				{
					Log.LogError((object)"Couldn't find a stage to move to. Use the normal exit.");
				}
			}
		}

		private static void Broadcast(string text)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			Chat.SendBroadcastChat((ChatMessageBase)new SimpleChatMessage
			{
				baseToken = text
			});
		}
	}
	internal class WagerShrineVisuals : MonoBehaviour
	{
		private static readonly Color VoidPurple = new Color(0.66f, 0.24f, 1f);

		private Highlight outline;

		private Light glow;

		internal static bool VoiceEnabled = true;

		internal static float VoiceVolume = 1.5f;

		internal static float VoiceInterval = 20f;

		internal static float VoiceRange = 18f;

		private PurchaseInteraction purchase;

		private float nextVoiceTime;

		private VoiceLine lastLine;

		private bool silenced;

		internal static void Apply(GameObject shrine)
		{
			if (!Object.op_Implicit((Object)(object)shrine) || Object.op_Implicit((Object)(object)shrine.GetComponent<WagerShrineVisuals>()))
			{
				return;
			}
			try
			{
				shrine.AddComponent<WagerShrineVisuals>().Setup();
			}
			catch (Exception arg)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)$"Couldn't add the shrine outline. {arg}");
			}
		}

		private void Setup()
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			Renderer val = ((Component)this).GetComponent<Highlight>()?.targetRenderer;
			if (!Object.op_Implicit((Object)(object)val))
			{
				val = (from r in ((Component)this).GetComponentsInChildren<Renderer>()
					where r is MeshRenderer || r is SkinnedMeshRenderer
					select r).OrderByDescending(delegate(Renderer r)
				{
					//IL_0001: Unknown result type (might be due to invalid IL or missing references)
					//IL_0006: Unknown result type (might be due to invalid IL or missing references)
					//IL_0009: 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)
					Bounds bounds = r.bounds;
					Vector3 size = ((Bounds)(ref bounds)).size;
					return ((Vector3)(ref size)).sqrMagnitude;
				}).FirstOrDefault();
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				outline = ((Component)this).gameObject.AddComponent<Highlight>();
				outline.targetRenderer = val;
				outline.highlightColor = (HighlightColor)3;
				outline.CustomColor = VoidPurple;
				outline.strength = 1f;
				outline.isOn = true;
			}
			GameObject val2 = new GameObject("VoidWagerGlow");
			val2.transform.SetParent(((Component)this).transform, false);
			val2.transform.localPosition = Vector3.up * 3f;
			glow = val2.AddComponent<Light>();
			glow.type = (LightType)2;
			glow.color = VoidPurple;
			glow.range = 12f;
			glow.intensity = 3f;
			purchase = ((Component)this).GetComponent<PurchaseInteraction>();
			nextVoiceTime = Time.time + 2f;
			VoidCoinFlipPlugin.Log.LogInfo((object)string.Format("Shrine voice: ready on {0} ({1} line(s), available={2}).", ((Object)((Component)this).gameObject).name, ShrineVoice.Lines.Count, Object.op_Implicit((Object)(object)purchase) ? purchase.available.ToString() : "no purchase component"));
		}

		private void UpdateVoice()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			if (!VoiceEnabled || silenced || ShrineVoice.Lines.Count == 0)
			{
				return;
			}
			if (Object.op_Implicit((Object)(object)purchase) && !purchase.available)
			{
				VoidCoinFlipPlugin.Log.LogInfo((object)"Shrine voice: shrine no longer available, going quiet.");
				silenced = true;
				ShrineVoice.Stop();
			}
			else
			{
				if (Time.time < nextVoiceTime)
				{
					return;
				}
				LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
				CharacterBody val = ((firstLocalUser != null) ? firstLocalUser.cachedBody : null);
				if (!Object.op_Implicit((Object)(object)val))
				{
					return;
				}
				Vector3 val2 = ((Component)this).transform.position - val.corePosition;
				float magnitude = ((Vector3)(ref val2)).magnitude;
				if (!(magnitude > VoiceRange))
				{
					float num = Mathf.Clamp01(1f - (magnitude - 3f) / Mathf.Max(1f, VoiceRange - 3f));
					float volume = VoiceVolume * num;
					float pan = 0f;
					Camera main = Camera.main;
					if (Object.op_Implicit((Object)(object)main) && magnitude > 0.5f)
					{
						pan = Mathf.Clamp(Vector3.Dot(((Vector3)(ref val2)).normalized, ((Component)main).transform.right), -1f, 1f) * 0.8f;
					}
					List<VoiceLine> list = ((ShrineVoice.Lines.Count > 1) ? ShrineVoice.Lines.Where((VoiceLine l) => l != lastLine).ToList() : ShrineVoice.Lines);
					VoiceLine voiceLine = (lastLine = list[Random.Range(0, list.Count)]);
					ShrineVoice.Play(voiceLine, volume, pan);
					nextVoiceTime = Time.time + voiceLine.Length + VoiceInterval;
				}
			}
		}

		private void OnDestroy()
		{
			if (!silenced)
			{
				ShrineVoice.Stop();
			}
		}

		private void Update()
		{
			float num = 0.75f + 0.25f * Mathf.Sin(Time.time * 2.5f);
			if (Object.op_Implicit((Object)(object)outline))
			{
				outline.isOn = true;
				outline.strength = num;
			}
			if (Object.op_Implicit((Object)(object)glow))
			{
				glow.intensity = 2f + 1.5f * num;
			}
			UpdateVoice();
		}
	}
	[HarmonyPatch(typeof(PurchaseInteraction), "OnInteractionBegin")]
	internal static class ShrineActivatePatch
	{
		private static bool Prefix(PurchaseInteraction __instance, Interactor activator)
		{
			VoidCoinFlipPlugin instance = VoidCoinFlipPlugin.Instance;
			if ((Object)(object)instance == (Object)null || !NetworkServer.active)
			{
				return true;
			}
			if (MusicSilencer.WagerWon && VoidFieldsCompletion.IsCellVent(__instance))
			{
				__instance.SetAvailable(false);
				return false;
			}
			if (!instance.IsWagerShrine(__instance))
			{
				return true;
			}
			try
			{
				instance.OnShrineActivated(activator);
			}
			catch (Exception ex)
			{
				VoidCoinFlipPlugin.Log.LogError((object)ex);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(PauseManager), "TogglePauseScreen")]
	internal static class EscapeClosesCoinPopupPatch
	{
		private static bool Prefix()
		{
			VoidCoinFlipPlugin instance = VoidCoinFlipPlugin.Instance;
			if ((Object)(object)instance == (Object)null || !instance.IsDialogOpen)
			{
				return true;
			}
			instance.WalkAwayFromDialog();
			return false;
		}
	}
	[HarmonyPatch(typeof(PurchaseInteraction), "GetInteractability")]
	internal static class HideVentPromptPatch
	{
		private static void Postfix(PurchaseInteraction __instance, ref Interactability __result)
		{
			if (MusicSilencer.WagerWon && VoidFieldsCompletion.IsCellVent(__instance))
			{
				__result = (Interactability)0;
			}
		}
	}
	[HarmonyPatch]
	internal static class ChatCallPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return (from m in typeof(Chat).GetMethods(BindingFlags.Static | BindingFlags.Public)
				where m.Name == "SendBroadcastChat"
				select m).Where(delegate(MethodInfo m)
			{
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length >= 1 && parameters[0].ParameterType == typeof(ChatMessageBase);
			}).Cast<MethodBase>();
		}

		private static void Prefix(ChatMessageBase __0)
		{
			try
			{
				if (NetworkServer.active && !((Object)(object)VoidCoinFlipPlugin.Instance == (Object)null))
				{
					UserChatMessage val = (UserChatMessage)(object)((__0 is UserChatMessage) ? __0 : null);
					if (val != null && Object.op_Implicit((Object)(object)val.sender))
					{
						NetworkUser component = val.sender.GetComponent<NetworkUser>();
						VoidCoinFlipPlugin.Instance.OnChatMessage(component, val.text);
					}
				}
			}
			catch (Exception ex)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)ex);
			}
		}
	}
	internal static class VoidFieldsCompletion
	{
		private const string ArenaTypeName = "RoR2.ArenaMissionController";

		public static GameObject ReadyVent;

		public static void RemoveReadyVent()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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)
			//IL_0088: Expected O, but got Unknown
			if (!NetworkServer.active || !Object.op_Implicit((Object)(object)ReadyVent))
			{
				return;
			}
			GameObject readyVent = ReadyVent;
			ReadyVent = null;
			try
			{
				ArenaMissionController instance = ArenaMissionController.instance;
				GameObject val = (GameObject)(Object.op_Implicit((Object)(object)instance) ? /*isinst with value type is only supported in some contexts*/: null);
				if (Object.op_Implicit((Object)(object)val))
				{
					EffectManager.SpawnEffect(val, new EffectData
					{
						origin = readyVent.transform.position,
						scale = 10f
					}, true);
				}
			}
			catch (Exception ex)
			{
				VoidCoinFlipPlugin.Log.LogInfo((object)("Void Fields: no burst effect for the vent (" + ex.Message + ")."));
			}
			try
			{
				if (Object.op_Implicit((Object)(object)readyVent.GetComponent<NetworkIdentity>()))
				{
					NetworkServer.Destroy(readyVent);
				}
				else
				{
					Object.Destroy((Object)(object)readyVent);
				}
				VoidCoinFlipPlugin.Log.LogInfo((object)("Void Fields: removed the waiting vent " + ((Object)readyVent).name + "."));
			}
			catch (Exception ex2)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)("Void Fields: couldn't remove the waiting vent. " + ex2.Message));
			}
		}

		public static bool Finish()
		{
			if (!NetworkServer.active)
			{
				return false;
			}
			Type arenaType = AccessTools.TypeByName("RoR2.ArenaMissionController");
			object obj = FindArena(arenaType);
			if (obj == null)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)"Couldn't find the Void Fields controller.");
				StopFog();
				return false;
			}
			StopSpawning(arenaType, obj);
			ClearMonsters();
			ReadyVent = ((Object.op_Implicit((Object)(object)ArenaMissionController.instance) && ArenaMissionController.instance.nullWards != null) ? ((IEnumerable<GameObject>)ArenaMissionController.instance.nullWards).FirstOrDefault((Func<GameObject, bool>)((GameObject w) => Object.op_Implicit((Object)(object)w) && MusicSilencer.WardState(w) == "WardOnAndReady")) : null);
			CompleteCells(arenaType, obj);
			LockVents();
			SetRoundsCleared(arenaType, obj);
			bool result = EnterMissionCompleted(arenaType, obj);
			SpawnCompletionPortals(arenaType, obj);
			StopFog();
			return result;
		}

		private static object FindArena(Type arenaType)
		{
			if (arenaType == null)
			{
				return null;
			}
			return AccessTools.Property(arenaType, "instance")?.GetValue(null) ?? Object.FindObjectOfType(arenaType);
		}

		private static void SetRoundsCleared(Type arenaType, object arena)
		{
			int num = ((AccessTools.Field(arenaType, "totalRoundsMax")?.GetValue(arena) is int num2) ? num2 : 9);
			try
			{
				MethodInfo methodInfo = AccessTools.PropertySetter(arenaType, "Network_clearedRounds") ?? AccessTools.PropertySetter(arenaType, "clearedRounds");
				if (methodInfo != null)
				{
					methodInfo.Invoke(arena, new object[1] { num });
				}
				else
				{
					AccessTools.Field(arenaType, "_clearedRounds")?.SetValue(arena, num);
				}
				AccessTools.PropertySetter(arenaType, "currentRound")?.Invoke(arena, new object[1] { num });
				VoidCoinFlipPlugin.Log.LogInfo((object)$"Void Fields: rounds cleared set to {num}/{num}.");
			}
			catch (Exception ex)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)("Couldn't set the cleared-rounds counter. " + ex.Message));
			}
		}

		private static bool EnterMissionCompleted(Type arenaType, object arena)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.Inner(arenaType, "MissionCompleted");
				object? obj = AccessTools.Field(arenaType, "mainStateMachine")?.GetValue(arena);
				EntityStateMachine val = (EntityStateMachine)((obj is EntityStateMachine) ? obj : null);
				if (type == null || !Object.op_Implicit((Object)(object)val))
				{
					VoidCoinFlipPlugin.Log.LogWarning((object)"Couldn't find the Void Fields 'mission completed' state.");
					return false;
				}
				if (val.state == null || ((object)val.state).GetType() != type)
				{
					val.SetNextState((EntityState)Activator.CreateInstance(type));
				}
				VoidCoinFlipPlugin.Log.LogInfo((object)"Void Fields: switched to mission completed.");
				return true;
			}
			catch (Exception arg)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)$"Couldn't complete the Void Fields. {arg}");
				return false;
			}
		}

		private static void SpawnCompletionPortals(Type arenaType, object arena)
		{
			if (!(AccessTools.Field(arenaType, "completionPortalSpawners")?.GetValue(arena) is PortalSpawner[] array) || array.Length == 0)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)"Void Fields: no completion portal spawners found.");
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(PortalSpawner), "AttemptSpawnPortalServer", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.PropertySetter(typeof(PortalSpawner), "NetworkwillSpawn");
			if (methodInfo == null)
			{
				VoidCoinFlipPlugin.Log.LogWarning((object)"Void Fields: couldn't find how to spawn the completion portal.");
				return;
			}
			PortalSpawner[] array2 = array;
			foreach (PortalSpawner val in array2)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				try
				{
					object? obj = AccessTools.Field(typeof(PortalSpawner), "requiredExpansion")?.GetValue(val);
					ExpansionDef val2 = (ExpansionDef)((obj is ExpansionDef) ? obj : null);
					if (val2 != null && Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)Run.instance) && !Run.instance.IsExpansionEnabled(val2))
					{
						VoidCoinFlipPlugin.Log.LogInfo((object)("Void Fields: skipping " + ((Object)val).name + " (needs " + ((Object)val2).name + ")."));
					}
					else
					{
						methodInfo2?.Invoke(val, new object[1] { true });
						object obj2 = ((methodInfo.GetParameters().Length == 0) ? methodInfo.Invoke(val, null) : null);
						VoidCoinFlipPlugin.Log.LogInfo((object)string.Format("Void Fields: completion portal from {0} -> {1}.", ((Object)val).name, obj2 ?? "attempted"));
					}
				}
				catch (Exception ex)
				{
					VoidCoinFlipPlugin.Log.LogWarning((object)("Void Fields: couldn't spawn the portal from " + ((Object)val).name + ". " + (ex.InnerException?.Message ?? ex.Message)));
				}
			}
		}

		private static void StopFog()
		{
			Type type = AccessTools.TypeByName("RoR2.FogDamageController");
			if (type == null)
			{
				return;
			}
			int num = 0;
			foreach (Behaviour item in Object.FindObjectsOfType(type).OfType<Behaviour>())
			{
				if (item.enabled)
				{
					item.enabled = false;
					num++;
				}
			}
			if (num > 0)
			{
				VoidCoinFlipPlugin.Log.LogInfo((object)$"Turned off {num} void fog source(s).");
			}
		}

		private static void StopSpawning(Type arenaType, object arena)
		{
			if (AccessTools.Field(arenaType, "combatDirectors")?.GetValue(arena) is CombatDirector[] source)
			{
				{
					foreach (CombatDirector item in source.Where((CombatDirector d) => Object.op_Implicit((Object)(object)d)))
					{
						((Behaviour)item).enabled = false;
					}
					return;
				}
			}
			CombatDirector[] array = Object.FindObjectsOfType<CombatDirector>();
			for (int num = 0; num < array.Length; num++)
			{
				((Behaviour)array[num]).enabled = false;
			}
		}

		private static void ClearMonsters()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			foreach (CharacterMaster item in CharacterMaster.readOnlyInstancesList.ToList())
			{
				if (Object.op_Implicit((Object)(object)item) && (int)item.teamIndex == 2)
				{
					CharacterBody body = item.GetBody();
					if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.healthComponent) && body.healthComponent.alive)
					{
						body.healthComponent.Suicide((GameObject)null, (GameObject)null, default(DamageTypeCombo));
					}
				}
			}
		}

		public static void LockVents()
		{
			if (!NetworkServer.active)
			{
				return;
			}
			ArenaMissionController instance = ArenaMissionController.instance;
			if (!Object.op_Implicit((Object)(object)instance) || instance.nullWards == null)
			{
				return;
			}
			int num = 0;
			GameObject[] nullWards = instance.nullWards;
			foreach (GameObject val in nullWards)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				PurchaseInteraction[] componentsInChildren = val.GetComponentsInChildren<PurchaseInteraction>(true);
				foreach (PurchaseInteraction val2 in componentsInChildren)
				{
					if (val2.available)
					{
						val2.SetAvailable(false);
						num++;
					}
				}
			}
			if (num > 0)
			{
				VoidCoinFlipPlugin.Log.LogInfo((object)$"Void Fields: locked {num} cell vent(s).");
			}
		}

		public static bool IsCellVent(PurchaseInteraction pi)
		{
			ArenaMissionController instance = ArenaMissionController.instance;
			if (!Object.op_Implicit((Object)(object)pi) || !Object.op_Implicit((Object)(object)instance) || instance.nullWards == null)
			{
				return false;
			}
			return instance.nullWards.Any((GameObject w) => Object.op_Implicit((Object)(object)w) && ((Component)pi).transform.IsChildOf(w.transform));
		}

		private static void CompleteCells(Type arenaType, object arena)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			Type completeState = AccessTools.TypeByName("EntityStates.Missions.Arena.NullWard.Complete");
			if (completeState == null || !(AccessTools.Field(arenaType, "nullWards")?.GetValue(arena) is GameObject[] array))
			{
				return;
			}
			int num = 0;
			GameObject[] array2 = array;
			foreach (GameObject val in array2)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				try
				{
					EntityStateMachine[] components = val.GetComponents<EntityStateMachine>();
					EntityStateMachine val2 = ((IEnumerable<EntityStateMachine>)components).FirstOrDefault((Func<EntityStateMachine, bool>)((EntityStateMachine m) => m.state != null && ((object)m.state).GetType().Namespace == completeState.Namespace)) ?? components.FirstOrDefault();
					if (Object.op_Implicit((Object)(object)val2) && (val2.state == null || !(((object)val2.state).GetType() == completeState)))
					{
						val2.SetState((EntityState)Activator.CreateInstance(completeState));
						num++;
					}
				}
				catch (Exception ex)
				{
					VoidCoinFlipPlugin.Log.LogWarning((object)("Couldn't finish cell " + ((Object)val).name + ". " + ex.Message));
				}
			}
			VoidCoinFlipPlugin.Log.LogInfo((object)$"Void Fields: {num} cell(s) set to finished.");
		}
	}
	internal static class WinChime
	{
		private const string Candidates = "Play_UI_obj_nullWard_complete,Play_UI_teleporter_event_complete,Play_UI_achievementUnlock";

		public const string WinMarker = "The void trials are passed";

		private static string working;

		private static string lastSeen;

		public static void Play()
		{
			GameObject val = SoundSource();
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			string[] array = ((working == null) ? (from s in "Play_UI_obj_nullWard_complete,Play_UI_teleporter_event_complete,Play_UI_achievementUnlock".Split(',')
				select s.Trim() into s
				where s.Length > 0
				select s).ToArray() : new string[1] { working });
			foreach (string text in array)
			{
				uint num2 = 0u;
				try
				{
					num2 = Util.PlaySound(text, val);
				}
				catch (Exception ex)
				{
					VoidCoinFlipPlugin.Log.LogWarning((object)("Win chime '" + text + "' failed: " + ex.Message));
				}
				if (num2 != 0)
				{
					if (working == null)
					{
						VoidCoinFlipPlugin.Log.LogInfo((object)("Win chime: using '" + text + "'."));
					}
					working = text;
					return;
				}
				VoidCoinFlipPlugin.Log.LogInfo((object)("Win chime: the game has no sound named '" + text + "', trying the next one."));
			}
			VoidCoinFlipPlugin.Log.LogWarning((object)"Win chime: none of the game sounds were found.");
		}

		private static GameObject SoundSource()
		{
			LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
			CharacterBody val = ((firstLocalUser != null) ? firstLocalUser.cachedBody : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				return ((Component)val).gameObject;
			}
			if (Object.op_Implicit((Object)(object)Camera.main))
			{
				return ((Component)Camera.main).gameObject;
			}
			if (!Object.op_Implicit((Object)(object)RoR2Application.instance))
			{
				return null;
			}
			return ((Component)RoR2Application.instance).gameObject;
		}

		public static void OnChatChanged()
		{
			if (NetworkServer.active)
			{
				return;
			}
			ReadOnlyCollection<string> readOnlyLog = Chat.readOnlyLog;
			if (readOnlyLog != null && readOnlyLog.Count != 0)
			{
				string text = readOnlyLog[readOnlyLog.Count - 1];
				if (text != null && text.Contains("The void trials are passed") && text != lastSeen)
				{
					lastSeen = text;
					Play();
				}
			}
		}
	}
}