Decompiled source of IBGameplay v0.5.37

BepInEx/plugins/stasy-IBGameplay/IBGameplay.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Photon.Pun;
using Photon.Realtime;
using Stasy.PeakHostUi;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace IBGameplay
{
	internal sealed class BoundaryColliderHighlight
	{
		private const int Steps = 24;

		internal readonly BoxCollider Owner;

		private readonly GameObject visual;

		private readonly Mesh mesh;

		private readonly MeshRenderer renderer;

		private readonly Vector3[] vertices = (Vector3[])(object)new Vector3[625];

		private readonly Vector2[] uv = (Vector2[])(object)new Vector2[625];

		private readonly Color[] colors = (Color[])(object)new Color[625];

		internal BoundaryColliderHighlight(BoxCollider owner, Material material)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			Owner = owner;
			visual = new GameObject("IBGameplay boundary highlight");
			((Object)visual).hideFlags = (HideFlags)61;
			visual.layer = 0;
			visual.transform.SetParent(((Component)owner).transform, false);
			mesh = new Mesh();
			((Object)mesh).name = "IBGameplay nearby boundary surface";
			((Object)mesh).hideFlags = (HideFlags)61;
			mesh.MarkDynamic();
			visual.AddComponent<MeshFilter>().sharedMesh = mesh;
			renderer = visual.AddComponent<MeshRenderer>();
			((Renderer)renderer).sharedMaterial = material;
			((Renderer)renderer).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)renderer).receiveShadows = false;
			((Renderer)renderer).lightProbeUsage = (LightProbeUsage)0;
			((Renderer)renderer).reflectionProbeUsage = (ReflectionProbeUsage)0;
			((Renderer)renderer).enabled = false;
			int[] array = new int[3456];
			int num = 0;
			for (int i = 0; i < 24; i++)
			{
				for (int j = 0; j < 24; j++)
				{
					int num2 = i * 25 + j;
					int num3 = num2 + 1;
					int num4 = num2 + 24 + 1;
					int num5 = num4 + 1;
					array[num++] = num2;
					array[num++] = num4;
					array[num++] = num3;
					array[num++] = num3;
					array[num++] = num4;
					array[num++] = num5;
				}
			}
			mesh.vertices = vertices;
			mesh.triangles = array;
		}

		internal void Update(Vector3 eye)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Owner == (Object)null || !((Collider)Owner).enabled || !((Component)Owner).gameObject.activeInHierarchy || Vector3.Distance(eye, ((Collider)Owner).ClosestPoint(eye)) >= 10f)
			{
				((Renderer)renderer).enabled = false;
				return;
			}
			Transform transform = ((Component)Owner).transform;
			Vector3 lossyScale = transform.lossyScale;
			((Vector3)(ref lossyScale))..ctor(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z));
			if (lossyScale.x < 0.0001f || lossyScale.y < 0.0001f || lossyScale.z < 0.0001f)
			{
				((Renderer)renderer).enabled = false;
				return;
			}
			Vector3 size = Owner.size;
			Vector3 center = Owner.center;
			Vector3 val = transform.InverseTransformPoint(eye);
			int num = BoundaryProximityRules.ThinAxis(size.x * lossyScale.x, size.y * lossyScale.y, size.z * lossyScale.z);
			int num2 = (num + 1) % 3;
			int num3 = (num + 2) % 3;
			float num4 = ((((Vector3)(ref val))[num] >= ((Vector3)(ref center))[num]) ? 1f : (-1f));
			float num5 = ((Vector3)(ref center))[num] + num4 * (((Vector3)(ref size))[num] * 0.5f + 0.035f / ((Vector3)(ref lossyScale))[num]);
			float num6 = 10f;
			float num7 = Mathf.Max(((Vector3)(ref center))[num2] - ((Vector3)(ref size))[num2] * 0.5f, ((Vector3)(ref val))[num2] - num6 / ((Vector3)(ref lossyScale))[num2]);
			float num8 = Mathf.Min(((Vector3)(ref center))[num2] + ((Vector3)(ref size))[num2] * 0.5f, ((Vector3)(ref val))[num2] + num6 / ((Vector3)(ref lossyScale))[num2]);
			float num9 = Mathf.Max(((Vector3)(ref center))[num3] - ((Vector3)(ref size))[num3] * 0.5f, ((Vector3)(ref val))[num3] - num6 / ((Vector3)(ref lossyScale))[num3]);
			float num10 = Mathf.Min(((Vector3)(ref center))[num3] + ((Vector3)(ref size))[num3] * 0.5f, ((Vector3)(ref val))[num3] + num6 / ((Vector3)(ref lossyScale))[num3]);
			if (num7 >= num8 || num9 >= num10)
			{
				((Renderer)renderer).enabled = false;
				return;
			}
			bool flag = false;
			for (int i = 0; i <= 24; i++)
			{
				for (int j = 0; j <= 24; j++)
				{
					int num11 = i * 25 + j;
					Vector3 val2 = center;
					((Vector3)(ref val2))[num] = num5;
					((Vector3)(ref val2))[num2] = Mathf.Lerp(num7, num8, (float)j / 24f);
					((Vector3)(ref val2))[num3] = Mathf.Lerp(num9, num10, (float)i / 24f);
					vertices[num11] = val2;
					ref Vector2 reference = ref uv[num11];
					reference = new Vector2(((Vector3)(ref val2))[num2] * ((Vector3)(ref lossyScale))[num2] / 1.5f, ((Vector3)(ref val2))[num3] * ((Vector3)(ref lossyScale))[num3] / 1.5f);
					float num12 = BoundaryProximityRules.Alpha(Vector3.Distance(eye, transform.TransformPoint(val2)));
					ref Color reference2 = ref colors[num11];
					reference2 = new Color(1f, 0.32f, 0.035f, num12);
					flag = flag || num12 > 0f;
				}
			}
			mesh.vertices = vertices;
			mesh.uv = uv;
			mesh.colors = colors;
			mesh.RecalculateBounds();
			((Renderer)renderer).enabled = flag;
		}

		internal void Dispose()
		{
			if ((Object)(object)renderer != (Object)null)
			{
				((Renderer)renderer).enabled = false;
			}
			if ((Object)(object)visual != (Object)null)
			{
				Object.Destroy((Object)(object)visual);
			}
			if ((Object)(object)mesh != (Object)null)
			{
				Object.Destroy((Object)(object)mesh);
			}
		}

		internal string Diagnostic(Camera camera)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			float num = (((Object)(object)Owner == (Object)null) ? (-1f) : Vector3.Distance(((Component)camera).transform.position, ((Collider)Owner).ClosestPoint(((Component)camera).transform.position)));
			return "wall=" + (((Object)(object)Owner == (Object)null) ? "missing" : ((Object)Owner).name) + " distance=" + num.ToString("F2", CultureInfo.InvariantCulture) + " colliderLayer=" + (((Object)(object)Owner == (Object)null) ? (-1) : ((Component)Owner).gameObject.layer) + " visualLayer=" + (((Object)(object)visual == (Object)null) ? (-1) : visual.layer) + " cameraLayerVisible=" + ((Object)(object)visual != (Object)null && (camera.cullingMask & (1 << visual.layer)) != 0) + " enabled=" + ((Object)(object)renderer != (Object)null && ((Renderer)renderer).enabled) + " active=" + ((Object)(object)visual != (Object)null && visual.activeInHierarchy);
		}
	}
	internal static class BoundaryProximityRules
	{
		internal const float Distance = 10f;

		internal const float CellSize = 1.5f;

		internal const int VisualLayer = 0;

		internal static float Alpha(float distance)
		{
			if (float.IsNaN(distance) || float.IsInfinity(distance) || distance >= 10f)
			{
				return 0f;
			}
			float num = Math.Max(0f, Math.Min(1f, (10f - distance) / 8f));
			return 0.7f * num * num * (3f - 2f * num);
		}

		internal static int ThinAxis(float x, float y, float z)
		{
			if (!(x <= y) || !(x <= z))
			{
				if (!(y <= z))
				{
					return 2;
				}
				return 1;
			}
			return 0;
		}

		internal static bool Wall(string parent, string name)
		{
			if (string.Equals(parent, "EdgeWalls", StringComparison.Ordinal))
			{
				return string.Equals(name, "EdgeWall", StringComparison.Ordinal);
			}
			return false;
		}
	}
	internal sealed class BoundarySurfacePalette
	{
		internal readonly MeshRenderer Owner;

		internal readonly int Slot;

		private readonly MaterialPropertyBlock original = new MaterialPropertyBlock();

		private readonly MaterialPropertyBlock work = new MaterialPropertyBlock();

		private readonly bool hadIndexedBlock;

		internal BoundarySurfacePalette(MeshRenderer owner, int slot)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			Owner = owner;
			Slot = slot;
			((Renderer)owner).GetPropertyBlock(original, slot);
			hadIndexedBlock = !original.isEmpty;
		}

		internal void Apply(Color top, Color mid, Color bottom)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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)
			if (!((Object)(object)Owner == (Object)null))
			{
				if (hadIndexedBlock)
				{
					((Renderer)Owner).GetPropertyBlock(work, Slot);
				}
				else
				{
					((Renderer)Owner).GetPropertyBlock(work);
				}
				work.SetColor("SkyTopColor", top);
				work.SetColor("SkyMidColor", mid);
				work.SetColor("SkyBottomColor", bottom);
				((Renderer)Owner).SetPropertyBlock(work, Slot);
			}
		}

		internal void Restore()
		{
			if ((Object)(object)Owner != (Object)null && Slot < ((Renderer)Owner).sharedMaterials.Length)
			{
				((Renderer)Owner).SetPropertyBlock(hadIndexedBlock ? original : null, Slot);
			}
		}
	}
	internal static class CalderaBoundaryRules
	{
		internal static bool Active(bool running, bool loading, bool mapReady, bool calderaSegment, bool volcanoBiome)
		{
			if (running && !loading && mapReady && calderaSegment)
			{
				return volcanoBiome;
			}
			return false;
		}

		internal static bool Surface(string origin, string objectName, string shader)
		{
			if (string.Equals(origin, "Caldera", StringComparison.OrdinalIgnoreCase) && shader == "FogWall")
			{
				if (!string.Equals(objectName, "Fogwall_Prev", StringComparison.OrdinalIgnoreCase))
				{
					return string.Equals(objectName, "Fogwall_Next", StringComparison.OrdinalIgnoreCase);
				}
				return true;
			}
			return false;
		}
	}
	internal sealed class CampArrivalRules
	{
		private readonly HashSet<int> reached = new HashSet<int>();

		private int segment = -1;

		internal void Reset()
		{
			reached.Clear();
			segment = -1;
		}

		internal void UpdateSegment(int current)
		{
			if (segment != current)
			{
				reached.Clear();
				segment = current;
			}
		}

		internal void Reach(int actor)
		{
			if (actor > 0)
			{
				reached.Add(actor);
			}
		}

		internal bool Protected(int actor)
		{
			return reached.Contains(actor);
		}
	}
	internal static class CampfireGuard
	{
		[HarmonyPatch]
		private static class Interaction
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Campfire), "Interact", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Campfire), "Interact_CastFinished", (Type[])null, (Type[])null);
			}

			[HarmonyPriority(800)]
			[HarmonyPrefix]
			private static bool Prefix(Campfire __instance, Character __0)
			{
				return Allowed(__instance, __0);
			}
		}

		[HarmonyPatch]
		private static class Availability
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Campfire), "IsInteractible", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Campfire), "IsConstantlyInteractable", (Type[])null, (Type[])null);
			}

			[HarmonyPriority(0)]
			[HarmonyPostfix]
			private static void Postfix(Campfire __instance, Character __0, ref bool __result)
			{
				if (!Allowed(__instance, __0))
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(Campfire), "GetInteractionText")]
		private static class Prompt
		{
			[HarmonyPostfix]
			private static void Postfix(Campfire __instance, ref string __result)
			{
				if (Restricted && PhotonNetwork.InRoom && !__instance.Lit && !PhotonNetwork.IsMasterClient)
				{
					__result = "ЗАЖИГАЕТ ТОЛЬКО ХОСТ";
				}
			}
		}

		[HarmonyPatch(typeof(PhotonNetwork), "ExecuteRpc", new Type[]
		{
			typeof(Hashtable),
			typeof(Player)
		})]
		private static class NetworkLight
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static bool Prefix(Hashtable __0, Player __1)
			{
				if (!Restricted || !PhotonNetwork.InRoom)
				{
					return true;
				}
				IList<string> names = (((Object)(object)PhotonNetwork.PhotonServerSettings == (Object)null) ? null : PhotonNetwork.PhotonServerSettings.RpcList);
				string text = CampfireRules.RpcName((IDictionary<object, object>)__0, names);
				if (text != "Light_Rpc")
				{
					return true;
				}
				if (__0 == null || !((Dictionary<object, object>)(object)__0).TryGetValue((object)(byte)0, out object value) || !(value is int))
				{
					return true;
				}
				PhotonView photonView = PhotonNetwork.GetPhotonView((int)value);
				bool campfire = (Object)(object)photonView != (Object)null && (Object)(object)((Component)photonView).GetComponent<Campfire>() != (Object)null;
				return CampfireRules.AllowRpc(restricted: true, inRoom: true, campfire, text, (__1 != null) ? __1.ActorNumber : 0, Master);
			}
		}

		private static bool Restricted
		{
			get
			{
				if ((Object)(object)Plugin.Instance != (Object)null)
				{
					return Plugin.Instance.HostOnlyCampfires;
				}
				return false;
			}
		}

		private static int Master
		{
			get
			{
				if (!PhotonNetwork.InRoom || PhotonNetwork.MasterClient == null)
				{
					return 0;
				}
				return PhotonNetwork.MasterClient.ActorNumber;
			}
		}

		private static bool Allowed(Campfire fire, Character character)
		{
			return CampfireRules.CanInteract(Restricted, PhotonNetwork.InRoom, fire.Lit, Plugin.Actor(character), Master);
		}
	}
	internal static class CampfireRules
	{
		internal static bool IsHost(int actor, int master)
		{
			if (actor > 0 && master > 0)
			{
				return actor == master;
			}
			return false;
		}

		internal static bool CanInteract(bool restricted, bool inRoom, bool lit, int actor, int master)
		{
			if (restricted && inRoom && !lit)
			{
				return IsHost(actor, master);
			}
			return true;
		}

		internal static bool AllowRpc(bool restricted, bool inRoom, bool campfire, string method, int sender, int master)
		{
			if (restricted && inRoom && campfire && !(method != "Light_Rpc"))
			{
				return IsHost(sender, master);
			}
			return true;
		}

		internal static string RpcName(IDictionary<object, object> data, IList<string> names)
		{
			if (data == null)
			{
				return null;
			}
			if (data.TryGetValue((byte)5, out var value))
			{
				if (!(value is byte) || names == null)
				{
					return null;
				}
				int num = (byte)value;
				if (num >= names.Count)
				{
					return null;
				}
				return names[num];
			}
			if (!data.TryGetValue((byte)3, out value))
			{
				return null;
			}
			return value as string;
		}
	}
	internal sealed class ChainAdapter
	{
		private static ChainAdapter current;

		private BaseUnityPlugin engine;

		private Type type;

		private MethodInfo stableKey;

		private FieldInfo enabled;

		private FieldInfo groupSize;

		private bool wasActive;

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

		private readonly Harmony runtime = new Harmony("stasy.ibgameplay.chain");

		private static bool Active
		{
			get
			{
				if ((Object)(object)Plugin.Instance != (Object)null && PhotonNetwork.InRoom && Plugin.Instance.State.Mode == Mode.Chains)
				{
					return Plugin.Instance.State.Armed;
				}
				return false;
			}
		}

		internal void TakeControl(Assembly assembly, Harmony guards)
		{
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_0415: Unknown result type (might be due to invalid IL or missing references)
			//IL_0428: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Expected O, but got Unknown
			//IL_0435: Expected O, but got Unknown
			current = this;
			engine = Chainloader.PluginInfos["patchnote.chainbound"].Instance;
			this.type = assembly.GetType("Chainbound.ChainboundPlugin", throwOnError: true);
			enabled = Field("_roomEnabled");
			groupSize = Field("_roomMaxGroupSize");
			stableKey = Method("GetUniquePlayerStableKey");
			string[] array = new string[3] { "ClearChainLines", "ClearProxyChain", "ClearApiGroups" };
			foreach (string name in array)
			{
				Method(name);
			}
			guards.Patch((MethodBase)Method("RefreshRoomSettings"), (HarmonyMethod)null, Hook("RoomSettings"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			guards.Patch((MethodBase)Method("ShouldRunHostEnabledGameplay"), Hook("Gameplay"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			guards.Patch((MethodBase)Method("get_ApiEnabled"), Hook("Gameplay"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			guards.Patch((MethodBase)Method("GetEffectiveChainOrder"), Hook("Order"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			guards.Patch((MethodBase)Method("GetPublishedChainOrder"), Hook("Order"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			guards.Patch((MethodBase)Method("IsPlayerOptedIntoChain"), (HarmonyMethod)null, Hook("Participant"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			guards.Patch((MethodBase)Method("CanLinkConnect"), (HarmonyMethod)null, Hook("LinkOnlySelectedLivingPair"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			guards.Patch((MethodBase)Method("HasInstalledCompatibleMod"), Hook("Compatible"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			guards.Patch((MethodBase)Method("DrawStatusOverlay"), Hook("ShowOverlay"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Type nestedType = this.type.GetNestedType("<>c", BindingFlags.NonPublic);
			MethodInfo methodInfo = ((nestedType == null) ? null : nestedType.GetMethod("<CollectActiveChainMembers>b__257_0", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			if (methodInfo == null)
			{
				throw new MissingMethodException("Chainbound member collection predicate");
			}
			guards.Patch((MethodBase)methodInfo, (HarmonyMethod)null, Hook("Participant"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			string[] array2 = new string[4] { "MaintainHostChainOrderIfNeeded", "ShuffleChainOrder", "UpdateHostShuffleInput", "HandleCampfireSegmentAdvanced" };
			foreach (string name2 in array2)
			{
				guards.Patch((MethodBase)Method(name2), Hook("SuppressAutoGroups"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			string[] array3 = new string[2] { "CharacterMovementCheckFallDamagePatch", "CharacterClimbingCheckFallDamagePatch" };
			foreach (string text in array3)
			{
				Type nestedType2 = this.type.GetNestedType(text, BindingFlags.NonPublic);
				if (nestedType2 == null)
				{
					throw new MissingMemberException(this.type.FullName, text);
				}
				guards.Patch((MethodBase)nestedType2.GetMethod("Prefix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), (HarmonyMethod)null, (HarmonyMethod)null, Hook("ReplaceFallDamageCall"), (HarmonyMethod)null, (HarmonyMethod)null);
			}
			new Harmony("patchnote.chainbound").UnpatchSelf();
			Type[] types = assembly.GetTypes();
			foreach (Type type in types)
			{
				if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length == 0)
				{
					continue;
				}
				if (type.Name == "CharacterAfflictionsAddStatusPatch")
				{
					MethodInfo methodInfo2 = AccessTools.Method(typeof(CharacterAfflictions), "AddStatus", new Type[7]
					{
						typeof(STATUSTYPE),
						typeof(float),
						typeof(bool),
						typeof(bool),
						typeof(bool),
						typeof(bool),
						typeof(bool)
					}, (Type[])null);
					if (methodInfo2 == null)
					{
						throw new MissingMethodException("CharacterAfflictions.AddStatus current signature");
					}
					runtime.Patch((MethodBase)methodInfo2, new HarmonyMethod(AccessTools.Method(type, "Prefix", (Type[])null, (Type[])null)), new HarmonyMethod(AccessTools.Method(type, "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					runtime.CreateClassProcessor(type).Patch();
				}
			}
			Refresh();
			Clear();
			Plugin.Instance.LoggerForAdapter("Chainbound original rules bound to current PEAK API.");
		}

		private FieldInfo Field(string name)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic);
			if (field == null)
			{
				throw new MissingFieldException(type.FullName, name);
			}
			return field;
		}

		private MethodInfo Method(string name)
		{
			MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (method == null)
			{
				throw new MissingMethodException(type.FullName, name);
			}
			return method;
		}

		private static HarmonyMethod Hook(string name)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			return new HarmonyMethod(typeof(ChainAdapter), name, (Type[])null);
		}

		private static bool Gameplay(ref bool __result)
		{
			__result = Active;
			return false;
		}

		private static bool SuppressAutoGroups()
		{
			return false;
		}

		private static bool ShowOverlay(string __0)
		{
			return __0 != "Chainbound Lobby Check";
		}

		private static bool Compatible(Player __0, ref bool __1, ref bool __result)
		{
			if (!Active)
			{
				return true;
			}
			__1 = false;
			__result = Plugin.Instance.PeerReady(__0);
			return false;
		}

		private static void Participant(Player __0, ref bool __result)
		{
			if (current != null)
			{
				current.ReleaseDeadPairs();
			}
			__result = __result && Active && current != null && __0 != null && ChainSelection.IsLinked(Plugin.Instance.State, __0.ActorNumber, current.released);
		}

		private static void LinkOnlySelectedLivingPair(Character __0, Character __1, ref bool __result)
		{
			if (current != null)
			{
				current.ReleaseDeadPairs();
			}
			__result = __result && Active && current != null && (Object)(object)__0 != (Object)null && (Object)(object)__1 != (Object)null && ChainSelection.CanConnect(Plugin.Instance.State, Plugin.Actor(__0), Plugin.Actor(__1), current.released);
		}

		private void ReleaseDeadPairs()
		{
			if (!Active)
			{
				return;
			}
			Pair[] pairs = Plugin.Instance.State.Pairs;
			foreach (Pair pair in pairs)
			{
				if (!released.Contains(pair.A))
				{
					Character val = Plugin.CharacterFor(pair.A);
					Character val2 = Plugin.CharacterFor(pair.B);
					if (((Object)(object)val != (Object)null && (Object)(object)val.data != (Object)null && val.data.dead) || ((Object)(object)val2 != (Object)null && (Object)(object)val2.data != (Object)null && val2.data.dead))
					{
						released.Add(pair.A);
						Plugin.Instance.LoggerForAdapter("Chained pair released after death: " + pair.A + "/" + pair.B + ". Other pairs remain linked.");
					}
				}
			}
		}

		private static void RoomSettings()
		{
			if (current != null)
			{
				current.Refresh();
			}
		}

		private static IEnumerable<CodeInstruction> ReplaceFallDamageCall(IEnumerable<CodeInstruction> instructions)
		{
			int replaced = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				MethodInfo call = instruction.operand as MethodInfo;
				if (call != null && call.DeclaringType.FullName == "Chainbound.ChainboundPlugin" && call.Name == "ApplySpeedBasedFallDamage")
				{
					instruction.opcode = OpCodes.Call;
					instruction.operand = typeof(ChainAdapter).GetMethod("ApplyCurrentFallDamage", BindingFlags.Static | BindingFlags.NonPublic);
					replaced++;
				}
				yield return instruction;
			}
			if (replaced != 1)
			{
				throw new InvalidOperationException("Expected exactly one Chainbound legacy fall-damage call.");
			}
		}

		private static void ApplyCurrentFallDamage(object engine, Character character, float extraSpeed, string source)
		{
			if (!Active || current == null || (Object)(object)character == (Object)null || !character.IsLocal || (Object)(object)character.data == (Object)null || character.refs == null || (Object)(object)character.refs.afflictions == (Object)null)
			{
				return;
			}
			object[] array = new object[2] { character, 0f };
			bool flag = (bool)current.Method("TryGetRecentCenterDownSpeed").Invoke(engine, array);
			float num = Mathf.Max(extraSpeed, flag ? ((float)array[1]) : Mathf.Max(0f, 0f - character.data.avarageVelocity.y));
			float num2 = (float)current.Field("_roomFallDamageMinSpeed").GetValue(engine);
			float num3 = (float)current.Field("_roomFallDamageMaxSpeed").GetValue(engine);
			float num4 = (float)current.Field("_roomFallDamageMultiplier").GetValue(engine);
			float num5 = Mathf.Clamp01(Mathf.Pow(Mathf.InverseLerp(Mathf.Min(num2, num3 - 0.1f), num3, num), 1.5f) * num4);
			if (!(num5 < 0.025f) && !(bool)current.Method("ShouldSuppressProtectedLandingDamage").Invoke(engine, new object[4] { character, num5, num, source }))
			{
				if (num5 > 0.3f)
				{
					character.refs.view.RPC("RPCA_Fall", (RpcTarget)0, new object[1] { num5 * 5f });
				}
				character.refs.afflictions.AddStatus((STATUSTYPE)0, num5 * Ascents.fallDamageMultiplier / Mathf.Max(0.001f, Ascents.etcDamageMultiplier), false, true, true, false, false);
			}
		}

		private static bool Order(ref List<string> __result)
		{
			__result = new List<string>();
			if (!Active || current == null)
			{
				return false;
			}
			current.ReleaseDeadPairs();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			Dictionary<int, string> dictionary = new Dictionary<int, string>();
			foreach (Player item in from p in PhotonNetwork.PlayerList
				where ChainSelection.IsLinked(Plugin.Instance.State, p.ActorNumber, current.released)
				orderby p.ActorNumber
				select p)
			{
				dictionary[item.ActorNumber] = (string)current.stableKey.Invoke(null, new object[2] { item, hashSet });
			}
			__result = ChainSelection.MakeOrder(Plugin.Instance.State, dictionary);
			return false;
		}

		internal void Refresh()
		{
			if (!((Object)(object)engine == (Object)null) && !(enabled == null))
			{
				bool active = Active;
				ReleaseDeadPairs();
				enabled.SetValue(engine, active);
				groupSize.SetValue(engine, 2);
				Field("_roomHasChainboundHost").SetValue(engine, PhotonNetwork.InRoom);
				if (!active && wasActive)
				{
					Clear();
				}
				wasActive = active;
			}
		}

		internal void Clear()
		{
			released.Clear();
			if (!((Object)(object)engine == (Object)null) && !(type == null))
			{
				enabled.SetValue(engine, false);
				string[] array = new string[3] { "ClearChainLines", "ClearProxyChain", "ClearApiGroups" };
				foreach (string name in array)
				{
					Method(name).Invoke(engine, null);
				}
				string[] array2 = new string[2] { "_chainFallProtectedUntil", "_chainClimbDamageProtectedUntil" };
				foreach (string name2 in array2)
				{
					Field(name2).SetValue(engine, 0f);
				}
				wasActive = false;
			}
		}

		internal void Dispose()
		{
			Clear();
			runtime.UnpatchSelf();
		}
	}
	internal static class ChainSelection
	{
		internal static bool IsLinked(ScenarioState state, int actor, ISet<int> released)
		{
			if (state.Mode == Mode.Chains && state.Armed)
			{
				return state.Pairs.Any((Pair p) => p.Contains(actor) && !released.Contains(p.A));
			}
			return false;
		}

		internal static bool CanConnect(ScenarioState state, int a, int b, ISet<int> released)
		{
			if (a != b && IsLinked(state, a, released))
			{
				return state.Pairs.Any((Pair p) => p.Contains(a) && p.Contains(b));
			}
			return false;
		}

		internal static List<string> MakeOrder(ScenarioState state, IDictionary<int, string> keys)
		{
			List<string> list = new List<string>();
			if (state.Mode != Mode.Chains || !state.Armed)
			{
				return list;
			}
			Pair[] pairs = state.Pairs;
			foreach (Pair pair in pairs)
			{
				list.Add(keys.ContainsKey(pair.A) ? keys[pair.A] : ("ibgameplay.missing." + pair.A));
				list.Add(keys.ContainsKey(pair.B) ? keys[pair.B] : ("ibgameplay.missing." + pair.B));
			}
			return list;
		}
	}
	internal static class ConfigJson
	{
		private static JsonSerializerSettings Settings()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			JsonSerializerSettings val = new JsonSerializerSettings();
			val.ContractResolver = (IContractResolver)new DefaultContractResolver();
			val.ConstructorHandling = (ConstructorHandling)1;
			val.TypeNameHandling = (TypeNameHandling)0;
			val.MissingMemberHandling = (MissingMemberHandling)1;
			val.CheckAdditionalContent = true;
			val.MaxDepth = 32;
			val.Culture = CultureInfo.InvariantCulture;
			val.DateParseHandling = (DateParseHandling)0;
			return val;
		}

		internal static T Read<T>(string json)
		{
			return JsonConvert.DeserializeObject<T>(json, Settings());
		}

		internal static string Write<T>(T value)
		{
			return JsonConvert.SerializeObject((object)value, (Formatting)1, Settings());
		}
	}
	internal static class EggCompatibility
	{
		[HarmonyPatch(typeof(Item), "Awake")]
		private static class Flags
		{
			[HarmonyPostfix]
			[HarmonyPriority(800)]
			private static void Postfix(Item __instance)
			{
				if (Active && Plugin.IsEgg(__instance))
				{
					Plugin.Instance.PrepareEgg(__instance);
				}
			}
		}

		private static MethodInfo serialize;

		internal static bool Active
		{
			get
			{
				if ((Object)(object)Plugin.Instance != (Object)null)
				{
					return Plugin.Instance.State.Mode == Mode.Egg;
				}
				return false;
			}
		}

		internal static bool ProtectGroundEgg(Item __0)
		{
			if (Active)
			{
				return !Plugin.IsEgg(__0);
			}
			return true;
		}

		internal static bool OriginalEggHud()
		{
			return !Active;
		}

		internal static bool WorldMarker(Component __instance)
		{
			return !Active;
		}

		internal static bool GrantHands(Player player, Item prefab, ItemInstanceData data, out ItemSlot slot, bool egg = true)
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			slot = null;
			if (!PhotonNetwork.IsMasterClient || (Object)(object)player == (Object)null || (Object)(object)prefab == (Object)null || data == null || player.tempFullSlot == null || !player.tempFullSlot.IsEmpty())
			{
				return false;
			}
			if (egg)
			{
				Plugin.Instance.PrepareEgg(prefab);
			}
			if (serialize == null)
			{
				serialize = typeof(InventorySyncData).GetInterfaces().Single((Type t) => t.FullName == "Zorro.Core.Serizalization.IBinarySerializable").GetMethods(BindingFlags.Static | BindingFlags.Public)
					.Single((MethodInfo m) => m.Name == "ToManagedArray")
					.MakeGenericMethod(typeof(InventorySyncData));
			}
			player.tempFullSlot.SetItem(prefab, data);
			try
			{
				byte[] array = (byte[])serialize.Invoke(null, new object[1] { (object)new InventorySyncData(player.itemSlots, player.backpackSlot, player.tempFullSlot) });
				((MonoBehaviourPun)player).photonView.RPC("SyncInventoryRPC", (RpcTarget)1, new object[2] { array, false });
				slot = player.tempFullSlot;
			}
			catch
			{
				player.tempFullSlot.SetItem((Item)null, (ItemInstanceData)null);
				throw;
			}
			return true;
		}
	}
	internal static class EggSlotPresence
	{
		internal static bool Contains(ItemSlot slot, string guid)
		{
			if (slot != null && !slot.IsEmpty() && !string.IsNullOrEmpty(guid))
			{
				return Plugin.GuidOf(slot.data) == guid;
			}
			return false;
		}
	}
	internal sealed class EggWorldHud
	{
		private sealed class Entry
		{
			internal TextMeshProUGUI Marker;

			internal TextMeshProUGUI Heading;

			internal TextMeshProUGUI Names;

			internal Image Accent;

			internal RectTransform Card;

			internal int Frame;
		}

		private GameObject root;

		private RectTransform canvasRect;

		private TextMeshProUGUI source;

		private readonly Dictionary<string, Entry> entries = new Dictionary<string, Entry>();

		private int frame;

		internal bool Begin(bool active)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			if (!active)
			{
				Clear();
				return false;
			}
			if ((Object)(object)root == (Object)null)
			{
				Clear();
				AscentUI val = Object.FindFirstObjectByType<AscentUI>();
				if ((Object)(object)val == (Object)null || (Object)(object)val.text == (Object)null || (Object)(object)((TMP_Text)val.text).font == (Object)null)
				{
					return false;
				}
				source = val.text;
				root = new GameObject("IBGameplayEggWorldHud", new Type[3]
				{
					typeof(RectTransform),
					typeof(Canvas),
					typeof(CanvasScaler)
				});
				Canvas component = root.GetComponent<Canvas>();
				component.renderMode = (RenderMode)0;
				component.sortingOrder = 100;
				CanvasScaler component2 = root.GetComponent<CanvasScaler>();
				component2.uiScaleMode = (ScaleMode)1;
				component2.referenceResolution = new Vector2(1920f, 1080f);
				component2.matchWidthOrHeight = 0.5f;
				canvasRect = root.GetComponent<RectTransform>();
			}
			frame++;
			foreach (Entry value in entries.Values)
			{
				((Component)value.Marker).gameObject.SetActive(false);
				((Component)value.Card).gameObject.SetActive(false);
			}
			return true;
		}

		internal void Marker(string guid, int pair, Color color, Vector2 screen)
		{
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)root == (Object)null) && !string.IsNullOrEmpty(guid))
			{
				if (!entries.TryGetValue(guid, out var value))
				{
					value = new Entry();
					value.Marker = TextAt("EggMarker", root.transform, new Vector2(180f, 60f), 22f);
					value.Card = RectAt("EggPairCard", root.transform, new Vector2(400f, 92f));
					Image val = ((Component)value.Card).gameObject.AddComponent<Image>();
					((Graphic)val).color = new Color(0.12f, 0.22f, 0.23f, 0.38f);
					((Graphic)val).raycastTarget = false;
					RectTransform val2 = RectAt("TeamAccent", (Transform)(object)value.Card, new Vector2(4f, 72f));
					val2.anchoredPosition = new Vector2(-190f, 0f);
					value.Accent = ((Component)val2).gameObject.AddComponent<Image>();
					((Graphic)value.Accent).raycastTarget = false;
					value.Heading = TextAt("EggPairTitle", (Transform)(object)value.Card, new Vector2(362f, 32f), 24f);
					((TMP_Text)value.Heading).rectTransform.anchoredPosition = new Vector2(4f, 22f);
					((TMP_Text)value.Heading).enableAutoSizing = true;
					((TMP_Text)value.Heading).fontSizeMin = 18f;
					((TMP_Text)value.Heading).fontSizeMax = 24f;
					value.Names = TextAt("EggPairNames", (Transform)(object)value.Card, new Vector2(362f, 44f), 20f);
					((TMP_Text)value.Names).rectTransform.anchoredPosition = new Vector2(4f, -15f);
					((TMP_Text)value.Names).enableAutoSizing = true;
					((TMP_Text)value.Names).fontSizeMin = 16f;
					((TMP_Text)value.Names).fontSizeMax = 20f;
					((Component)value.Card).gameObject.SetActive(false);
					entries.Add(guid, value);
				}
				value.Frame = frame;
				((TMP_Text)value.Marker).text = "●\nЯЙЦО · " + (pair + 1);
				((Graphic)value.Marker).color = color;
				((TMP_Text)value.Marker).rectTransform.anchoredPosition = Local(screen, new Vector2(98f, 110f));
				((Component)value.Marker).gameObject.SetActive(true);
			}
		}

		internal void Popup(string guid, string title, string names, Color color, Vector2 screen)
		{
			//IL_0031: 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_0088: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)root == (Object)null) && entries.TryGetValue(guid, out var value))
			{
				((TMP_Text)value.Heading).text = title;
				((Graphic)value.Heading).color = color;
				((TMP_Text)value.Names).text = names;
				((Graphic)value.Names).color = new Color(0.96f, 0.92f, 0.78f, 1f);
				((Graphic)value.Accent).color = new Color(color.r, color.g, color.b, 0.7f);
				Vector2 anchoredPosition = Local(screen, new Vector2(208f, 54f));
				anchoredPosition.y += 46f;
				Rect rect = canvasRect.rect;
				float num = Mathf.Max(0f, ((Rect)(ref rect)).height * 0.5f - 54f);
				anchoredPosition.y = Mathf.Clamp(anchoredPosition.y, 0f - num, num);
				value.Card.anchoredPosition = anchoredPosition;
				((Component)value.Card).gameObject.SetActive(true);
			}
		}

		private Vector2 Local(Vector2 screen, Vector2 margin)
		{
			//IL_0006: 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_0022: 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)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, screen, (Camera)null, ref val);
			Rect rect = canvasRect.rect;
			float num = Mathf.Max(0f, ((Rect)(ref rect)).width * 0.5f - margin.x);
			Rect rect2 = canvasRect.rect;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(num, Mathf.Max(0f, ((Rect)(ref rect2)).height * 0.5f - margin.y));
			return new Vector2(Mathf.Clamp(val.x, 0f - val2.x, val2.x), Mathf.Clamp(val.y, 0f - val2.y, val2.y));
		}

		private static RectTransform RectAt(string name, Transform parent, Vector2 size)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_003d: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_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)
			//IL_005a: 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(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			Vector2 val2 = (component.pivot = new Vector2(0.5f, 0.5f));
			Vector2 anchorMin = (component.anchorMax = val2);
			component.anchorMin = anchorMin;
			component.sizeDelta = size;
			return component;
		}

		private TextMeshProUGUI TextAt(string name, Transform parent, Vector2 size, float fontSize)
		{
			//IL_0002: 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)
			RectTransform val = RectAt(name, parent, size);
			TextMeshProUGUI val2 = ((Component)val).gameObject.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val2).font = ((TMP_Text)source).font;
			((TMP_Text)val2).fontSharedMaterial = ((TMP_Text)source).fontSharedMaterial;
			((TMP_Text)val2).fontStyle = ((TMP_Text)source).fontStyle;
			((TMP_Text)val2).fontSize = fontSize;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
			((Graphic)val2).raycastTarget = false;
			((TMP_Text)val2).richText = false;
			((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)1;
			return val2;
		}

		internal void End()
		{
			string[] array = (from p in entries
				where p.Value.Frame != frame
				select p.Key).ToArray();
			foreach (string key in array)
			{
				Entry entry = entries[key];
				Object.Destroy((Object)(object)((Component)entry.Marker).gameObject);
				Object.Destroy((Object)(object)((Component)entry.Card).gameObject);
				entries.Remove(key);
			}
		}

		internal void Clear()
		{
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root);
			}
			root = null;
			canvasRect = null;
			source = null;
			entries.Clear();
			frame = 0;
		}
	}
	internal sealed class Engines
	{
		internal Assembly Egg;

		internal Assembly Coop;

		internal Assembly Chain;

		internal readonly GhostAdapter Ghost = new GhostAdapter();

		internal readonly PitchBlackAdapter PitchBlack = new PitchBlackAdapter();

		private BaseUnityPlugin coopPlugin;

		private readonly Harmony eggHarmony = new Harmony("stasy.ibgameplay.egg");

		private readonly Harmony piggyHarmony = new Harmony("stasy.ibgameplay.piggyback");

		private readonly ChainAdapter chain = new ChainAdapter();

		private readonly List<Character[]> carried = new List<Character[]>();

		private readonly Dictionary<ItemUIData, bool[]> itemFlags = new Dictionary<ItemUIData, bool[]>();

		private static readonly string[] Modules = new string[11]
		{
			"ShareAlive", "ShareDeath", "ItemFix", "ShareInventory", "SeparateRole", "Rolechanger", "Latejoin", "Piggyback", "OnlyEven", "ShareStamina",
			"Freepass"
		};

		private bool coopRunning;

		private Type eggTimerType;

		private PropertyInfo eggRemaining;

		internal void TakeControl(Harmony guards, BaseUnityPlugin owner)
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Expected O, but got Unknown
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Expected O, but got Unknown
			//IL_0442: Unknown result type (might be due to invalid IL or missing references)
			//IL_044f: Expected O, but got Unknown
			//IL_04af: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bd: Expected O, but got Unknown
			PitchBlack.TakeControl(guards);
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "IBGameplay.Bootstrap");
			if (assembly == null || !(bool)assembly.GetType("IBGameplayStartup.Patcher", throwOnError: true).GetProperty("Ready").GetValue(null, null))
			{
				throw new InvalidOperationException((assembly == null) ? "IBGameplay startup helper missing. Install the complete package, including BepInEx/patchers." : "IBGameplay startup helper loaded but did not activate. Check the controller DLL location and startup log.");
			}
			Egg = ((object)Chainloader.PluginInfos["sapphire009.watchegg"].Instance).GetType().Assembly;
			coopPlugin = Chainloader.PluginInfos["com.peak.coopmod"].Instance;
			Coop = ((object)coopPlugin).GetType().Assembly;
			Chain = ((object)Chainloader.PluginInfos["patchnote.chainbound"].Instance).GetType().Assembly;
			new Harmony("sapphire009.watchegg").UnpatchSelf();
			new Harmony("Sapphire009.StaminaFull").UnpatchSelf();
			string[] array = new string[3] { "sapphire009.watchegg", "Sapphire009.StaminaFull", "com.peak.coopmod" };
			foreach (string key in array)
			{
				((Behaviour)Chainloader.PluginInfos[key].Instance).enabled = false;
			}
			string[] modules = Modules;
			foreach (string type in modules)
			{
				Call(Coop, type, "Shutdown");
			}
			chain.TakeControl(Chain, guards);
			PinnedDependencies.Verify(Egg, "14AE4F3AF4C8FE8C34F95A87F19D07904CC1E8406DD1A604072667BEE0CD2852");
			PinnedDependencies.Verify(Coop, "02D0CFDE61FCD6C9D91DC66A8FBFF7ECB55B770EE2ABF9F38D5A79C873843C47");
			PinnedDependencies.Verify(Chain, "793C8933E329D856121F3F689EB6E8A9C0FF06728D012E3183EEF89EC61779D4");
			Ghost.TakeControl(guards);
			eggTimerType = Egg.GetType("Sapphire009.WatchEgg.Timer", throwOnError: true);
			eggRemaining = AccessTools.Property(eggTimerType, "RemainingTime");
			if (eggRemaining == null || eggRemaining.PropertyType != typeof(float) || !eggRemaining.CanRead)
			{
				throw new MissingMemberException("WatchEgg timer RemainingTime");
			}
			string[] array2 = new string[2] { "TimerUI", "CountUI" };
			foreach (string text in array2)
			{
				guards.Patch((MethodBase)MethodLookup.Require(Egg.GetType("Sapphire009.WatchEgg." + text, throwOnError: true), "OnGUI", 0, isStatic: false), new HarmonyMethod(typeof(EggCompatibility), "OriginalEggHud", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			guards.Patch((MethodBase)MethodLookup.Require(Egg.GetType("Sapphire009.WatchEgg.BrighteningEgg", throwOnError: true), "OnGUI", 0, isStatic: false), new HarmonyMethod(typeof(EggCompatibility), "WorldMarker", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Require(Coop, "Rolechanger", "ApplyCarriedState", 2);
			Require(Coop, "Rolechanger", "ClearCarriedState", 2);
			string[] modules2 = Modules;
			foreach (string type2 in modules2)
			{
				Require(Coop, type2, "Shutdown", 0);
			}
			string[] array3 = new string[7] { "Freepass", "SeparateRole", "ShareStamina", "ShareInventory", "ItemFix", "ShareDeath", "ShareAlive" };
			foreach (string type3 in array3)
			{
				Require(Coop, type3, "Initialize", 1);
			}
			Type type4 = Egg.GetType("StaminaFull", throwOnError: true);
			guards.Patch((MethodBase)type4.GetMethod("CanApplyProtection", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Character) }, null), (HarmonyMethod)null, new HarmonyMethod(typeof(Guards), "Protection", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			string[] array4 = new string[2] { "EnsureTimedDespawn", "TryMarkSpawnedItem" };
			foreach (string text2 in array4)
			{
				guards.Patch((MethodBase)Require(Egg, "Sapphire009.WatchEgg.NoEat", text2, (!(text2 == "EnsureTimedDespawn")) ? 1 : 2), new HarmonyMethod(typeof(EggCompatibility), "ProtectGroundEgg", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		internal void StartEgg()
		{
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Expected O, but got Unknown
			Item item = default(Item);
			if (ItemDatabase.TryGetItem("NestEgg", ref item))
			{
				RememberFlags(item);
			}
			string[] source = new string[12]
			{
				"StaminaFull", "Sapphire009.WatchEgg.AutoDrop", "Sapphire009.WatchEgg.NoInventory", "Sapphire009.WatchEgg.Timer", "Sapphire009.WatchEgg.TimerSound", "Sapphire009.WatchEgg.TimerUI", "Sapphire009.WatchEgg.BrighteningEgg", "Sapphire009.WatchEgg.NoBody", "Sapphire009.WatchEgg.NoEat", "Sapphire009.WatchEgg.Magma",
				"Sapphire009.WatchEgg.MagmaLavaHitPlayerPatch", "Sapphire009.WatchEgg.MagmaStaminaFullProtectionPatch"
			};
			Type[] types = Egg.GetTypes();
			foreach (Type type in types)
			{
				Func<string, bool> predicate = (string root) => type.FullName == root || type.FullName.StartsWith(root + "+", StringComparison.Ordinal);
				if (!source.Any(predicate) || type.FullName == "Sapphire009.WatchEgg.NoInventory+EquipSlotPatch" || type.FullName == "Sapphire009.WatchEgg.NoInventory+SyncInventoryPatch" || type.FullName == "Sapphire009.WatchEgg.NoInventory+PickupAcceptedPatch")
				{
					continue;
				}
				if (type.FullName == "StaminaFull+CharacterAfflictionsAddStatusPatch" || type.FullName == "StaminaFull+CharacterAfflictionsAddThornPositionPatch" || type.FullName == "StaminaFull+CharacterAfflictionsAddThornIndexPatch")
				{
					Type[] types2 = (type.FullName.EndsWith("AddStatusPatch", StringComparison.Ordinal) ? new Type[7]
					{
						typeof(STATUSTYPE),
						typeof(float),
						typeof(bool),
						typeof(bool),
						typeof(bool),
						typeof(bool),
						typeof(bool)
					} : (type.FullName.EndsWith("PositionPatch", StringComparison.Ordinal) ? new Type[2]
					{
						typeof(Vector3),
						typeof(int)
					} : new Type[3]
					{
						typeof(int),
						typeof(ushort),
						typeof(bool)
					}));
					string text = (type.FullName.EndsWith("AddStatusPatch", StringComparison.Ordinal) ? "AddStatus" : "AddThorn");
					MethodInfo method = typeof(CharacterAfflictions).GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types2, null);
					if (method == null)
					{
						throw new MissingMethodException("CharacterAfflictions", text);
					}
					eggHarmony.Patch((MethodBase)method, new HarmonyMethod(type.GetMethod("Prefix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0)
				{
					eggHarmony.CreateClassProcessor(type).Patch();
				}
			}
			Call(Egg, "Sapphire009.WatchEgg.TimerUI", "EnsureTimerUIExists");
		}

		internal void RememberFlags(Item item)
		{
			if ((Object)(object)item != (Object)null && item.UIData != null && !itemFlags.ContainsKey(item.UIData))
			{
				itemFlags.Add(item.UIData, new bool[2]
				{
					item.UIData.canPocket,
					item.UIData.canDrop
				});
			}
		}

		internal bool TryEggTime(Item item, out float seconds)
		{
			seconds = 0f;
			if (!Plugin.IsEgg(item) || eggTimerType == null || eggRemaining == null)
			{
				return false;
			}
			Component component = ((Component)item).GetComponent(eggTimerType);
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			seconds = (float)eggRemaining.GetValue(component, null);
			return true;
		}

		internal void StartCoop()
		{
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			coopRunning = true;
			Type[] types = Coop.GetTypes();
			foreach (Type type in types)
			{
				if (type.FullName.StartsWith("Piggyback+CharacterCarrying_", StringComparison.Ordinal))
				{
					piggyHarmony.CreateClassProcessor(type).Patch();
				}
			}
			string[] array = new string[7] { "Freepass", "ShareStamina", "SeparateRole", "ShareInventory", "ItemFix", "ShareDeath", "ShareAlive" };
			foreach (string type2 in array)
			{
				Call(Coop, type2, "Initialize", coopPlugin);
			}
			Type type3 = Coop.GetType("ShareInventory+Campfire_LightRpc_SafeDrop_Patch", throwOnError: true);
			MethodInfo[] methods = type3.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name == "Prefix" || methodInfo.Name == "Postfix")
				{
					MethodBase[] array2 = Harmony.GetAllPatchedMethods().ToArray();
					foreach (MethodBase methodBase in array2)
					{
						new Harmony("com.peak.coopmod.shareinventory").Unpatch(methodBase, methodInfo);
					}
				}
			}
		}

		internal void Link(Character carrier, Character rider)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!carried.Any((Character[] p) => (Object)(object)p[0] == (Object)(object)carrier && (Object)(object)p[1] == (Object)(object)rider))
			{
				if (carrier.IsLocal)
				{
					carrier.refs.items.EquipSlot(Optionable<byte>.None);
				}
				Call(Coop, "Rolechanger", "ApplyCarriedState", carrier, rider);
				carried.Add((Character[])(object)new Character[2] { carrier, rider });
				Call(Coop, "Piggyback", "RegisterLockedPair", carrier, rider);
				Call(Coop, "ShareDeath", "RegisterPair", carrier, rider);
				Call(Coop, "ShareAlive", "RegisterPair", carrier, rider);
			}
		}

		internal void Stop()
		{
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			List<Exception> list = new List<Exception>();
			try
			{
				PitchBlack.Stop();
			}
			catch (Exception item)
			{
				list.Add(item);
			}
			try
			{
				Ghost.Stop();
			}
			catch (Exception item2)
			{
				list.Add(item2);
			}
			chain.Clear();
			if (coopRunning)
			{
				string[] modules = Modules;
				foreach (string type in modules)
				{
					try
					{
						Call(Coop, type, "Shutdown");
					}
					catch (Exception item3)
					{
						list.Add(item3);
					}
				}
				coopRunning = false;
			}
			piggyHarmony.UnpatchSelf();
			HashSet<string> hashSet = new HashSet<string>();
			MethodBase[] array = Harmony.GetAllPatchedMethods().ToArray();
			foreach (MethodBase methodBase in array)
			{
				Patches patchInfo = Harmony.GetPatchInfo(methodBase);
				if (patchInfo == null)
				{
					continue;
				}
				foreach (string owner in patchInfo.Owners)
				{
					if (owner.StartsWith("com.peak.coopmod.", StringComparison.Ordinal))
					{
						hashSet.Add(owner);
					}
				}
			}
			foreach (string item5 in hashSet)
			{
				new Harmony(item5).UnpatchSelf();
			}
			foreach (Character[] item6 in carried)
			{
				if ((Object)(object)item6[0] != (Object)null && (Object)(object)item6[1] != (Object)null)
				{
					try
					{
						Call(Coop, "Rolechanger", "ClearCarriedState", item6[0], item6[1]);
					}
					catch (Exception item4)
					{
						list.Add(item4);
					}
				}
			}
			carried.Clear();
			eggHarmony.UnpatchSelf();
			foreach (KeyValuePair<ItemUIData, bool[]> itemFlag in itemFlags)
			{
				itemFlag.Key.canPocket = itemFlag.Value[0];
				itemFlag.Key.canDrop = itemFlag.Value[1];
			}
			itemFlags.Clear();
			if (Egg != null)
			{
				MonoBehaviour[] array2 = Resources.FindObjectsOfTypeAll<MonoBehaviour>();
				foreach (MonoBehaviour val in array2)
				{
					if ((Object)(object)val != (Object)null && ((object)val).GetType().Assembly == Egg && !(val is BaseUnityPlugin))
					{
						((Behaviour)val).enabled = false;
						Object.Destroy((Object)(object)val);
					}
				}
			}
			if (list.Count != 0)
			{
				throw new AggregateException("Engine cleanup failed", list);
			}
		}

		internal void Dispose()
		{
			PitchBlack.Stop();
			if (Egg != null && Coop != null)
			{
				Stop();
			}
			chain.Dispose();
		}

		internal bool GiveEgg(Player player, string guid)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Item prefab = default(Item);
			if (!ItemDatabase.TryGetItem("NestEgg", ref prefab))
			{
				return false;
			}
			ItemInstanceData val = new ItemInstanceData(Guid.ParseExact(guid, "N"));
			ItemInstanceDataHandler.AddInstanceData(val);
			ItemSlot slot;
			bool flag = EggCompatibility.GrantHands(player, prefab, val, out slot);
			if (flag)
			{
				((MonoBehaviourPun)player.character.refs.items).photonView.RPC("OnPickupAccepted", ((MonoBehaviourPun)player).photonView.Owner, new object[1] { (byte)250 });
			}
			return flag;
		}

		internal void RefreshChain()
		{
			chain.Refresh();
		}

		internal static MethodInfo Require(Assembly assembly, string type, string name, int count)
		{
			MethodInfo[] array = (from m in assembly.GetType(type, throwOnError: true).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
				where m.Name == name && m.GetParameters().Length == count
				select m).ToArray();
			if (array.Length != 1)
			{
				throw new MissingMethodException(type, name);
			}
			return array[0];
		}

		internal static object Call(Assembly assembly, string type, string name, params object[] args)
		{
			return Require(assembly, type, name, args.Length).Invoke(null, args);
		}
	}
}
namespace Stasy.PeakHostUi
{
	internal sealed class FogControl : IDisposable
	{
		private readonly FogVisualValue<float> height = new FogVisualValue<float>();

		private readonly FogVisualValue<bool> builtin = new FogVisualValue<bool>();

		private readonly FogVisualValue<float> band = new FogVisualValue<float>();

		private readonly FogVisualValue<float> haze = new FogVisualValue<float>();

		private bool subscribed;

		private bool pitchBlack;

		internal static bool DirectorDisabled
		{
			get
			{
				if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null && PhotonNetwork.MasterClient != null && ((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"ibgameplay.director.fog.v1", out object value))
				{
					return FogRules.Disabled(value, PhotonNetwork.MasterClient.ActorNumber);
				}
				return false;
			}
		}

		internal static bool SetDirectorDisabled(bool disabled)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom == null)
			{
				return false;
			}
			Room currentRoom = PhotonNetwork.CurrentRoom;
			Hashtable val = new Hashtable();
			((Dictionary<object, object>)(object)val).Add((object)"ibgameplay.director.fog.v1", (object)new object[2]
			{
				PhotonNetwork.LocalPlayer.ActorNumber,
				disabled
			});
			return currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
		}

		internal void Start()
		{
			if (!subscribed)
			{
				subscribed = true;
				RenderPipelineManager.beginCameraRendering += BeforeCamera;
			}
		}

		internal void SetPitchBlack(bool active)
		{
			pitchBlack = active;
			Tick();
		}

		internal void Tick()
		{
			bool directorDisabled = DirectorDisabled;
			if (FogRules.Suppressed(directorDisabled, pitchBlack && PhotonNetwork.InRoom))
			{
				height.Apply(() => Shader.GetGlobalFloat("HeightFogAmount"), delegate(float value)
				{
					Shader.SetGlobalFloat("HeightFogAmount", value);
				}, 0f);
				builtin.Apply(() => RenderSettings.fog, delegate(bool value)
				{
					RenderSettings.fog = value;
				}, forced: false);
			}
			else
			{
				Restore();
			}
			if (directorDisabled)
			{
				band.Apply(() => Shader.GetGlobalFloat("BandFogAmount"), delegate(float value)
				{
					Shader.SetGlobalFloat("BandFogAmount", value);
				}, 0f);
				haze.Apply(() => Shader.GetGlobalFloat("_GlobalHazeAmount"), delegate(float value)
				{
					Shader.SetGlobalFloat("_GlobalHazeAmount", value);
				}, 0f);
			}
			else
			{
				band.Restore(delegate(float value)
				{
					Shader.SetGlobalFloat("BandFogAmount", value);
				});
				haze.Restore(delegate(float value)
				{
					Shader.SetGlobalFloat("_GlobalHazeAmount", value);
				});
			}
		}

		private void BeforeCamera(ScriptableRenderContext context, Camera camera)
		{
			Tick();
		}

		private void Restore()
		{
			height.Restore(delegate(float value)
			{
				Shader.SetGlobalFloat("HeightFogAmount", value);
			});
			builtin.Restore(delegate(bool value)
			{
				RenderSettings.fog = value;
			});
			band.Restore(delegate(float value)
			{
				Shader.SetGlobalFloat("BandFogAmount", value);
			});
			haze.Restore(delegate(float value)
			{
				Shader.SetGlobalFloat("_GlobalHazeAmount", value);
			});
		}

		public void Dispose()
		{
			if (subscribed)
			{
				RenderPipelineManager.beginCameraRendering -= BeforeCamera;
			}
			subscribed = false;
			pitchBlack = false;
			Restore();
		}
	}
	internal static class FogRules
	{
		internal const string Key = "ibgameplay.director.fog.v1";

		internal static bool Disabled(object raw, int master)
		{
			object[] array = raw as object[];
			if (master > 0 && array != null && array.Length == 2 && array[0] is int && (int)array[0] == master && array[1] is bool)
			{
				return (bool)array[1];
			}
			return false;
		}

		internal static bool Suppressed(bool director, bool pitchBlack)
		{
			return director;
		}
	}
	internal sealed class FogVisualValue<T>
	{
		private bool captured;

		private T baseline;

		internal void Apply(Func<T> read, Action<T> write, T forced)
		{
			T x = read();
			if (!captured || !EqualityComparer<T>.Default.Equals(x, forced))
			{
				baseline = x;
			}
			captured = true;
			write(forced);
		}

		internal void Restore(Action<T> write)
		{
			if (captured)
			{
				write(baseline);
				captured = false;
			}
		}
	}
}
namespace IBGameplay
{
	internal static class GameplayHudText
	{
		internal const float EggHoldSeconds = 20f;

		internal static string Observer => "Вы наблюдатель";

		internal static string ModeCaption(string mode)
		{
			if (!string.IsNullOrEmpty(mode))
			{
				return "Режим: " + mode;
			}
			return "";
		}

		internal static string GhostPickup(double seconds)
		{
			if (double.IsNaN(seconds) || double.IsInfinity(seconds) || seconds <= 0.0)
			{
				return "Подбор предмета доступен";
			}
			int num = (int)Math.Min(180.0, Math.Ceiling(seconds));
			return "Следующий предмет: " + (num / 60).ToString(CultureInfo.InvariantCulture) + ":" + (num % 60).ToString("00", CultureInfo.InvariantCulture);
		}

		internal static float EggSeconds(float seconds)
		{
			if (!float.IsNaN(seconds) && !float.IsInfinity(seconds))
			{
				return Math.Max(0f, Math.Min(20f, seconds));
			}
			return 0f;
		}

		internal static string EggCountdown(float seconds)
		{
			return "До сброса яйца: " + EggSeconds(seconds).ToString("0.0", CultureInfo.InvariantCulture).Replace('.', ',') + " с";
		}

		internal static int EggUrgency(float seconds)
		{
			seconds = EggSeconds(seconds);
			if (!(seconds <= 2f))
			{
				if (!(seconds <= 5f))
				{
					return 0;
				}
				return 1;
			}
			return 2;
		}

		internal static string EggCounters(int[] counts)
		{
			if (counts == null || counts.Length == 0)
			{
				return "";
			}
			if (counts.Length == 1)
			{
				return "Разбито яиц: " + Math.Max(0, counts[0]).ToString(CultureInfo.InvariantCulture);
			}
			return "Разбито яиц\n" + string.Join("\n", counts.Select((int count, int index) => "<color=#" + PairIdentity.ColorRgb(index).ToString("X6", CultureInfo.InvariantCulture) + ">Пара " + (index + 1).ToString(CultureInfo.InvariantCulture) + ": " + Math.Max(0, count).ToString(CultureInfo.InvariantCulture) + "</color>").ToArray());
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("stasy.idlpro")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("stasy.pairmodes")]
	[BepInPlugin("stasy.ibgameplay", "IBGameplay", "0.5.37")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("com.github.Quaree.Chained")]
	[BepInIncompatibility("stasy.watchegg.rescue")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private const string GhostReportKey = "ibgameplay.ghostreport.v1";

		private const string OptionsKey = "ibgameplay.options.v1";

		private const string OffersKey = "ibgameplay.offers.v4";

		private const string TimerKey = "ibgameplay.timer.v1";

		private const byte ActionEvent = 182;

		private const string ActionTag = "IBGameplay.actions.v1";

		public const string Id = "stasy.ibgameplay";

		public const string Version = "0.5.37";

		public const int HostPanelApiVersion = 1;

		private const string StateKey = "ibgameplay.state.v1";

		private const string ReadyKey = "ibgameplay.version";

		private const string AckKey = "ibgameplay.ack";

		private const string ErrorKey = "ibgameplay.error";

		private const string LoadedKey = "ibgameplay.loaded.v1";

		private readonly CampArrivalRules campArrivals = new CampArrivalRules();

		private readonly GhostTargetRotation ghostTargets = new GhostTargetRotation();

		private ActionCatalog actionCatalog;

		private string catalogNotice = "Набор действий ещё не загружен.";

		private readonly GhostObserverHud ghostObserverHud = new GhostObserverHud();

		private GhostRoundReport hostGhostReport;

		private GhostRoundReport observedGhostReport;

		private string ghostReportWire;

		private string observedReportWire;

		private int observedReportHost;

		private int observedReportEpoch = -1;

		internal LiveOptions Options = new LiveOptions();

		private ConfigEntry<float> ghostRadius;

		private ConfigEntry<float> ghostSpeed;

		private ConfigEntry<float> ghostInterval;

		private ConfigEntry<float> ghostChoiceDuration;

		private ConfigEntry<float> lanternRange;

		private ConfigEntry<float> lanternPower;

		private ConfigEntry<float> campProtectionRadius;

		private ConfigEntry<bool> ghostOpponents;

		private ConfigEntry<bool> ghostDynamite;

		private readonly GhostChoiceView ghostChoiceView = new GhostChoiceView();

		private readonly ConfiguredOffers actionOffers = new ConfiguredOffers();

		private readonly SharedGhostRounds sharedRounds = new SharedGhostRounds();

		private readonly Random offerRandom = new Random();

		private readonly LiveHud liveHud = new LiveHud();

		private LoadBalancingClient actionClient;

		private object liveRoom;

		private int liveHost;

		private int offerEpoch = -1;

		private int offerSerial;

		private int popupSerial;

		private int requestSerial;

		private int selectedEnemy;

		private int selectedEnemy2;

		private float nextLivePoll;

		private float nextRequest;

		private string optionsWire;

		private string offersWire;

		private string actionNotice = "";

		private GameObject choiceCursorObject;

		private MenuWindow choiceCursor;

		private ConfiguredOffer localOffer;

		private bool choiceOpen;

		private string pendingEquip = "";

		private int equipEpoch;

		private float equipUntil;

		private float equipRetry;

		private bool liveSettingsOpen;

		private readonly HostPanel hostPanel = new HostPanel();

		private readonly MenuToggleGate menuToggleGate = new MenuToggleGate();

		internal static Plugin Instance;

		internal ScenarioState State = new ScenarioState();

		private readonly Harmony guards = new Harmony("stasy.ibgameplay");

		private readonly Engines engines = new Engines();

		private readonly HashSet<string> owned = new HashSet<string>();

		private readonly HashSet<string> counted = new HashSet<string>();

		private readonly Dictionary<string, float> brokenRecovery = new Dictionary<string, float>();

		private readonly Dictionary<string, float> missingEggs = new Dictionary<string, float>();

		private readonly Dictionary<string, string> recoveryBlocks = new Dictionary<string, string>();

		private readonly ModeHud modeHud = new ModeHud();

		private readonly EggWorldHud eggWorldHud = new EggWorldHud();

		private readonly FogControl fogControl = new FogControl();

		private bool rescueOpen;

		private readonly List<LineRenderer> lines = new List<LineRenderer>();

		private readonly PairEggCheckpoints<Vector3> checkpoints = new PairEggCheckpoints<Vector3>(delegate(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: 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)
			Vector3 val = a - b;
			return ((Vector3)(ref val)).sqrMagnitude;
		});

		private readonly List<Pair> draft = new List<Pair>();

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

		private Item[] visibleEggs = (Item[])(object)new Item[0];

		private ConfigEntry<KeyCode> menuKey;

		private ConfigEntry<KeyCode> rescueKey;

		private bool ready;

		private bool menuOpen;

		private bool busy;

		private bool linked;

		private bool metadataDirty;

		private int appliedEpoch = -1;

		private int blockedEpoch = -1;

		private int lastHost;

		private int lastRoster;

		private string lastWire = "";

		private string error = "";

		private string message = "Готово. Выберите пары и режим.";

		private string readyToken;

		private float nextPoll;

		private float nextCheckpoint;

		private float nextEggScan;

		private float nextMissingScan;

		private float noticeUntil;

		private int recoveryPair = -1;

		private int recoveryEpoch;

		private string recoveryGuid = "";

		private bool recoveryCheckpoint;

		private Rect window = new Rect(70f, 55f, 960f, 700f);

		private Vector2 scroll;

		private int choosingPair = -1;

		private bool choosingB;

		private GUIStyle label;

		private GUIStyle button;

		private GUIStyle title;

		private GUIStyle panel;

		private GUIStyle choicePanel;

		private GUIStyle sectionPanel;

		private GUIStyle modePanel;

		private GUIStyle goodLabel;

		private GUIStyle badLabel;

		private GUIStyle stopButton;

		private GUIStyle[] pairLabels;

		private Texture2D background;

		private Texture2D buttonBackground;

		private Texture2D buttonHover;

		private Texture2D buttonActive;

		private Texture2D sectionBackground;

		private Texture2D modeBackground;

		private Texture2D stopBackground;

		private Texture2D stopHover;

		private Texture2D stopActive;

		private GameObject cursorObject;

		private MenuWindow cursorProxy;

		private ConfigEntry<KeyCode> sharedTimerKey;

		private ConfigEntry<int> sharedTimerSeconds;

		private float nextSharedTimerToggle;

		private readonly TaskSession tasks = new TaskSession();

		private readonly TaskHud taskHud = new TaskHud();

		private TaskCatalog taskCatalog;

		private object taskRoom;

		private int taskHost;

		private int selectedTask;

		private string taskScene = "";

		private string taskNotice = "";

		private string selectedTaskId = "";

		private string taskGeneration = "";

		private string taskRequest = "";

		private bool taskSynced;

		private bool taskListOpen;

		private Vector2 taskListScroll;

		private float nextTaskRequest;

		private static string GhostActionsPath => Path.Combine(Paths.ConfigPath, "IBGameplay.GhostActions.json");

		private static bool EnemyBridgeAvailable
		{
			get
			{
				if (Chainloader.PluginInfos.TryGetValue("stasy.PEAKDirector", out var value) && (Object)(object)value.Instance != (Object)null)
				{
					return ((object)value.Instance).GetType().GetMethod("SpawnForGhostAction") != null;
				}
				return false;
			}
		}

		public bool HostMenuOpen
		{
			get
			{
				if (menuOpen)
				{
					return Host;
				}
				return false;
			}
		}

		public float HostPanelWidth => UiLayout.DirectorWidth(((Rect)(ref window)).width);

		public float HostPanelHeight => Math.Max(1f, ((Rect)(ref window)).height - 120f);

		internal bool HostOnlyCampfires => ready;

		private bool Host
		{
			get
			{
				if (PhotonNetwork.InRoom)
				{
					return PhotonNetwork.IsMasterClient;
				}
				return false;
			}
		}

		private bool InRun
		{
			get
			{
				//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)
				Scene activeScene = SceneManager.GetActiveScene();
				return ((Scene)(ref activeScene)).name.StartsWith("Level_", StringComparison.OrdinalIgnoreCase);
			}
		}

		private static string TasksPath => Path.Combine(Paths.ConfigPath, "IBGameplay.Tasks.json");

		private void UpdateCampArrivals()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected I4, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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)
			if (!Host || !InRun || LoadingScreenHandler.loading || !MapHandler.ExistsAndInitialized)
			{
				return;
			}
			campArrivals.UpdateSegment((int)MapHandler.CurrentSegmentNumber);
			Campfire currentCampfire;
			try
			{
				currentCampfire = MapHandler.CurrentCampfire;
			}
			catch (NullReferenceException)
			{
				return;
			}
			if ((Object)(object)currentCampfire == (Object)null)
			{
				return;
			}
			float value = campProtectionRadius.Value;
			Player[] playerList = PhotonNetwork.PlayerList;
			foreach (Player val in playerList)
			{
				Character val2 = CharacterFor(val.ActorNumber);
				if (Living(val2))
				{
					Vector3 val3 = val2.Center - currentCampfire.Center();
					if (((Vector3)(ref val3)).sqrMagnitude <= value * value)
					{
						campArrivals.Reach(val.ActorNumber);
					}
				}
			}
		}

		private int[] EligibleEnemies(int actor, int targetCount = 1)
		{
			int partner = GhostRules.Partner(State, actor);
			if (partner == 0 || !Options.Opponents)
			{
				return new int[0];
			}
			return (from a in (from p in State.Pairs
					select p.A into a
					where a != partner && !State.IsGhostObserver(a) && Living(CharacterFor(a)) && !campArrivals.Protected(a) && ghostTargets.Allowed(actor, a, targetCount)
					select a).Distinct()
				orderby a
				select a).ToArray();
		}

		private string ActionUnavailable(int actor, ActionDefinition action)
		{
			if (GhostRules.Partner(State, actor) == 0)
			{
				return "Пара больше не активна.";
			}
			if (action.IsEnemy)
			{
				if (!Options.Opponents)
				{
					return "Взаимодействие с соперниками выключено.";
				}
				if (EligibleEnemies(actor, action.targetCount).Length < action.targetCount)
				{
					if (action.targetCount != 2)
					{
						return "Нет доступных соперников: живых, до костра, не выбранных уже дважды подряд.";
					}
					return "Нужны 2 живых соперника, ещё не достигших костра.";
				}
			}
			if (action.type == "spawn" && !EnemyBridgeAvailable)
			{
				return "У хоста недоступен PEAKDirector.";
			}
			if (action.type == "item")
			{
				if (!ResolveActionItem(action.item, out var prefab))
				{
					return "Предмет недоступен в текущей игре.";
				}
				if (action.target == "ghost" && !GhostRewardSupported(prefab))
				{
					return "Этот предмет призрак не может использовать.";
				}
			}
			return "";
		}

		private bool RefreshActionAvailability()
		{
			bool result = false;
			foreach (ConfiguredOffer item in actionOffers.All.Where((ConfiguredOffer o) => o.Pending))
			{
				string text = ActionUnavailable(item.Actor, item.Left);
				string text2 = ActionUnavailable(item.Actor, item.Right);
				int[] array = EligibleEnemies(item.Actor, 2);
				int num = ghostTargets.Excluded(item.Actor);
				if (item.LeftUnavailable != text || item.RightUnavailable != text2 || !item.Enemies.SequenceEqual(array) || item.SingleExcluded != num)
				{
					result = true;
				}
				item.LeftUnavailable = text;
				item.RightUnavailable = text2;
				item.Enemies = array;
				item.SingleExcluded = num;
			}
			return result;
		}

		private void ReloadGhostActions()
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			if (!Host)
			{
				return;
			}
			try
			{
				if (!File.Exists(GhostActionsPath))
				{
					Directory.CreateDirectory(Paths.ConfigPath);
					using FileStream stream = new FileStream(GhostActionsPath, FileMode.CreateNew, FileAccess.Write);
					using StreamWriter streamWriter = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
					streamWriter.Write(ConfigJson.Write(ActionCatalog.Defaults()));
				}
				if (new FileInfo(GhostActionsPath).Length > 262144)
				{
					throw new ArgumentException("Файл действий слишком большой (максимум 256 КБ).");
				}
				string json = File.ReadAllText(GhostActionsPath, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true));
				ActionCatalog actionCatalog = ConfigJson.Read<ActionCatalog>(json);
				if (actionCatalog == null)
				{
					throw new ArgumentException("Пустой JSON.");
				}
				actionCatalog.Validate();
				ActionDefinition[] actions = actionCatalog.actions;
				foreach (ActionDefinition actionDefinition in actions)
				{
					if (actionDefinition.type == "status")
					{
						ResolveActionStatus(actionDefinition.status);
					}
					if (actionDefinition.type == "item" && (Object)(object)SingletonAsset<ItemDatabase>.Instance != (Object)null && SingletonAsset<ItemDatabase>.Instance.itemLookup != null && SingletonAsset<ItemDatabase>.Instance.itemLookup.Count > 0)
					{
						if (!ResolveActionItem(actionDefinition.item, out var prefab))
						{
							throw new ArgumentException(actionDefinition.id + ": неизвестный предмет " + actionDefinition.item);
						}
						if (actionDefinition.target == "ghost" && !GhostRewardSupported(prefab))
						{
							throw new ArgumentException(actionDefinition.id + ": этот предмет выдавайте живому (target: partner или enemy); призрак использует еду, лечение и динамит.");
						}
					}
				}
				this.actionCatalog = actionCatalog;
				catalogNotice = "Действий: " + actionCatalog.actions.Length + ". Режим: " + actionCatalog.selectionMode + ". Общие раунды; новые правила — со следующего раунда.";
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Host action catalog loaded: " + GhostActionsPath + "; actions=" + actionCatalog.actions.Length + "; fixedPairs=" + actionCatalog.offers.Length));
				message = catalogNotice;
			}
			catch (Exception ex)
			{
				catalogNotice = "Ошибка файла действий: " + ex.Message + ((this.actionCatalog == null) ? ". Выбор действий недоступен до исправления файла." : ". Предыдущий набор сохранён.");
				message = catalogNotice;
				((BaseUnityPlugin)this).Logger.LogWarning((object)catalogNotice);
			}
		}

		private bool GhostRewardSupported(Item prefab)
		{
			if (!engines.Ghost.Feedable(prefab))
			{
				if (Options.Dynamite)
				{
					return (Object)(object)((Component)prefab).GetComponent<Dynamite>() != (Object)null;
				}
				return false;
			}
			return true;
		}

		private static STATUSTYPE ResolveActionStatus(string value)
		{
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			string name = (value ?? "").Trim();
			switch (name.ToLowerInvariant())
			{
			case "голод":
				name = "Hunger";
				break;
			case "отравление":
			case "яд":
				name = "Poison";
				break;
			case "травма":
				name = "Injury";
				break;
			case "холод":
				name = "Cold";
				break;
			case "жара":
				name = "Hot";
				break;
			case "сонливость":
				name = "Drowsy";
				break;
			case "проклятие":
				name = "Curse";
				break;
			}
			string text = Enum.GetNames(typeof(STATUSTYPE)).FirstOrDefault((string n) => string.Equals(n, name, StringComparison.OrdinalIgnoreCase));
			if (text == null)
			{
				throw new ArgumentException("Неизвестный статус: " + value);
			}
			return (STATUSTYPE)Enum.Parse(typeof(STATUSTYPE), text);
		}

		private static bool ResolveActionItem(string value, out Item prefab)
		{
			prefab = null;
			if ((Object)(object)SingletonAsset<ItemDatabase>.Instance == (Object)null || SingletonAsset<ItemDatabase>.Instance.itemLookup == null)
			{
				return false;
			}
			if (!ushort.TryParse(value, out var result))
			{
				return ItemDatabase.TryGetItem(value, ref prefab);
			}
			return ItemDatabase.TryGetItem(result, ref prefab);
		}

		private void GrantLivingActionItem(Character target, Item prefab)
		{
			//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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: 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)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: 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_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			if (!Host || !Living(target) || (Object)(object)target.player == (Object)null)
			{
				throw new InvalidOperationException("Получатель предмета недоступен.");
			}
			if (target.player.itemSlots != null && target.player.itemSlots.Take(3).Any((ItemSlot s) => s != null && s.IsEmpty()))
			{
				ItemSlot val = default(ItemSlot);
				if (!target.player.AddItem(prefab.itemID, (ItemInstanceData)null, ref val) || val == null || val.itemSlotID >= 3)
				{
					throw new InvalidOperationException("Выдача в инвентарь отклонена.");
				}
				return;
			}
			Vector3 val2 = target.Center + Vector3.up * 0.2f;
			RaycastHit val3 = default(RaycastHit);
			if (Physics.Raycast(target.Center + Vector3.up * 0.5f, Vector3.down, ref val3, 3f, LayerMask.GetMask(new string[2] { "Terrain", "Default" }), (QueryTriggerInteraction)1))
			{
				val2 = ((RaycastHit)(ref val3)).point + Vector3.up * 0.3f;
			}
			GameObject val4 = PhotonNetwork.InstantiateItemRoom(((Object)((Component)prefab).gameObject).name, val2, Quaternion.identity, true);
			if ((Object)(object)val4 == (Object)null)
			{
				throw new InvalidOperationException("Не удалось создать предмет рядом с получателем.");
			}
			Item component = val4.GetComponent<Item>();
			if ((Object)(object)component == (Object)null)
			{
				PhotonNetwork.Destroy(val4);
				throw new InvalidOperationException("Созданный объект не является предметом.");
			}
			component.SetKinematicNetworked(false, val2, Quaternion.identity);
		}

		private void ExportActionItems()
		{
			if (!Host || (Object)(object)SingletonAsset<ItemDatabase>.Instance == (Object)null || SingletonAsset<ItemDatabase>.Instance.itemLookup == null)
			{
				return;
			}
			try
			{
				IEnumerable<string> second = from p in SingletonAsset<ItemDatabase>.Instance.itemLookup
					where (Object)(object)p.Value != (Object)null
					orderby p.Key
					select p.Key + "\t" + ((Object)((Component)p.Value).gameObject).name;
				File.WriteAllLines(Path.Combine(Paths.ConfigPath, "IBGameplay.Items.txt"), new string[1] { "ID\titem (exact prefab name)" }.Concat(second), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				message = "Список ID и названий сохранён: BepInEx/config/IBGameplay.Items.txt";
			}
			catch (Exception ex)
			{
				message = "Не удалось сохранить список: " + ex.Message;
			}
		}

		private void StartGhostReport(int[] actors, ActionDefinition left, ActionDefinition right, double until, double next)
		{
			if (Host)
			{
				hostGhostReport = GhostRoundReport.Start(liveHost, State.Epoch, offerSerial, left.label, right.label, until, next, actors);
			}
		}

		private void RecordGhostDecision(int actor, int choice, ActionDefinition action, int target, int target2)
		{
			if (Host && hostGhostReport != null && hostGhostReport.Epoch == State.Epoch && hostGhostReport.Serial == offerSerial)
			{
				int target3 = ((action.target == "ghost") ? actor : ((action.target == "partner") ? GhostRules.Partner(State, actor) : target));
				hostGhostReport.Decide(actor, choice, target3, (action.targetCount == 2) ? target2 : 0);
			}
		}

		private void PublishGhostReport()
		{
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			if (!Host)
			{
				return;
			}
			if (hostGhostReport != null)
			{
				if (State.Mode != Mode.Ghost || !State.Armed || hostGhostReport.Epoch != State.Epoch)
				{
					hostGhostReport = null;
				}
				else
				{
					GhostRoundDecision[] rows = hostGhostReport.Rows;
					foreach (GhostRoundDecision row in rows)
					{
						row.Active = GhostRules.Partner(State, row.Actor) != 0 && PhotonNetwork.PlayerList.Any((Player p) => p.ActorNumber == row.Actor);
					}
				}
			}
			string text = ((hostGhostReport == null) ? null : hostGhostReport.Encode());
			if (!(text == ghostReportWire))
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				Hashtable val = new Hashtable();
				((Dictionary<object, object>)(object)val).Add((object)"ibgameplay.ghostreport.v1", (object)text);
				if (currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null))
				{
					ghostReportWire = text;
				}
			}
		}

		private void ResetGhostReport()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			bool flag = hostGhostReport != null || ghostReportWire != null;
			hostGhostReport = (observedGhostReport = null);
			observedReportWire = null;
			observedReportHost = 0;
			observedReportEpoch = -1;
			ghostObserverHud.Clear();
			if (Host && PhotonNetwork.CurrentRoom != null && (flag || (((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"ibgameplay.ghostreport.v1", out object value) && value != null)))
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				Hashtable val = new Hashtable();
				((Dictionary<object, object>)(object)val).Add((object)"ibgameplay.ghostreport.v1", (object)null);
				currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
			}
			ghostReportWire = null;
		}

		private void UpdateGhostObserverHud(float offset)
		{
			int actor = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : 0);
			if (!ready || !InRun || LoadingScreenHandler.loading || !PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null || PhotonNetwork.MasterClient == null || !GhostRoundReport.Observer(State, actor))
			{
				ghostObserverHud.Hide();
				return;
			}
			int actorNumber = PhotonNetwork.MasterClient.ActorNumber;
			object value;
			string text = (((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"ibgameplay.ghostreport.v1", out value) ? (value as string) : null);
			if (text != observedReportWire || actorNumber != observedReportHost || State.Epoch != observedReportEpoch)
			{
				observedReportWire = text;
				observedReportHost = actorNumber;
				observedReportEpoch = State.Epoch;
				GhostRoundReport.TryRead(text, actorNumber, State.Epoch, out observedGhostReport);
			}
			GhostRoundReport ghostRoundReport = (Host ? hostGhostReport : observedGhostReport);
			if (ghostRoundReport == null || ghostRoundReport.Host != actorNumber || ghostRoundReport.Epoch != State.Epoch || ghostRoundReport.Rows.Any((GhostRoundDecision row) => !State.Pairs.Any((Pair p) => p.B == row.Actor)))
			{
				ghostObserverHud.Hide();
				return;
			}
			ghostObserverHud.Show(ghostRoundReport, PhotonNetwork.Time, offset, GhostObserverName, (int ghost) => Array.FindIndex(State.Pairs, (Pair p) => p.B == ghost) + 1);
		}

		private static string GhostObserverName(int actor)
		{
			Player val = ((IEnumerable<Player>)PhotonNetwork.PlayerList).FirstOrDefault((Func<Player, bool>)((Player p) => p.ActorNumber == actor));
			string source = ((val == null || string.IsNullOrWhiteSpace(val.NickName)) ? ("Игрок #" + actor) : val.NickName);
			source = new string(source.Where((char c) => !char.IsControl(c)).ToArray());
			if (source.Length <= 36)
			{
				return source;
			}
			return source.Substring(0, 35) + "…";
		}

		private static bool FinitePose(Vector3 p, Quaternion q)
		{
			if (GhostOffers.Finite(p.x) && GhostOffers.Finite(p.y) && GhostOffers.Finite(p.z) && GhostOffers.Finite(q.x) && GhostOffers.Finite(q.y) && GhostOffers.Finite(q.z) && GhostOffers.Finite(q.w) && q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w > 0.5f)
			{
				return q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w < 1.5f;
			}
			return false;
		}

		private void DropOldGhostReward(Character c)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: 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_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_013a: 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)
			if (!Host || (Object)(object)c == (Object)null || (Object)(object)c.player == (Object)null || c.player.tempFullSlot == null)
			{
				throw new InvalidOperationException("Не удалось проверить руку призрака.");
			}
			if (!c.player.tempFullSlot.IsEmpty())
			{
				if ((Object)(object)c.player.tempFullSlot.prefab == (Object)null)
				{
					throw new InvalidOperationException("Предыдущий предмет ещё не загружен. Повторите выбор.");
				}
				string text = GuidOf(c.player.tempFullSlot.data);
				if (!Guid.TryParseExact(text, "N", out var _) || !GhostAdapter.Instance.RewardPose(c, out var position, out var rotation) || !FinitePose(position, rotation))
				{
					throw new InvalidOperationException("Положение призрака ещё не синхронизировано. Повторите выбор.");
				}
				if (!GhostAdapter.Instance.DropReplacement(c, text, position, rotation, master: true))
				{
					throw new InvalidOperationException("Предыдущий предмет ещё не синхронизирован. Повторите выбор.");
				}
				object[] obj = new object[7]
				{
					"IBGameplay.actions.v1",
					"replacement-drop",
					State.Epoch,
					Actor(c),
					text,
					position,
					rotation
				};
				RaiseEventOptions val = new RaiseEventOptions();
				val.Receivers = (ReceiverGroup)0;
				if (!PhotonNetwork.RaiseEvent((byte)182, (object)obj, val, SendOptions.SendReliable))
				{
					throw new InvalidOperationException("Старый предмет сброшен, но сеть недоступна. Повторите выбор после восстановления связи.");
				}
				GhostAdapter.Instance.ResetRewardSelection(c);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Ghost reward replaced actor=" + Actor(c) + "; old item dropped at flying ghost."));
			}
		}

		private void ReceiveGhostReplacement(EventData data, object[] p)
		{
			//IL_00c4: 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_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			if (data.Sender != PhotonNetwork.MasterClient.ActorNumber || Host || !InRun || LoadingScreenHandler.loading || State.Mode != Mode.Ghost || !State.Armed || p.Length != 7 || !(p[2] is int) || (int)p[2] != State.Epoch || !(p[3] is int) || !(p[4] is string) || !(p[5] is Vector3) || !(p[6] is Quaternion))
			{
				return;
			}
			int actor = (int)p[3];
			string text = (string)p[4];
			if (GhostRules.Partner(State, actor) != 0 && Guid.TryParseExact(text, "N", out var _) && FinitePose((Vector3)p[5], (Quaternion)p[6]))
			{
				Character c = CharacterFor(actor);
				if (GhostAdapter.Instance.DropReplacement(c, text, (Vector3)p[5], (Quaternion)p[6], master: false))
				{
					GhostAdapter.Instance.ResetRewardSelection(c);
				}
			}
		}

		internal void DrawGroundEggMarker(Item egg)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)egg == (Object)null || !((Component)egg).gameObject.activeInHierarchy || (int)egg.itemState != 0)
			{
				return;
			}
			string guid = GuidOf(egg.data);
			int num = Array.FindIndex(State.Pairs, (Pair p) => p.Egg == guid && guid != "");
			if (num < 0)
			{
				return;
			}
			Camera val = (((Object)(object)MainCamera.instance != (Object)null) ? ((Component)MainCamera.instance).GetComponent<Camera>() : Camera.main);
			if ((Object)(object)val == (Object)null)
			{
				val = Camera.main;
			}
			if (!((Object)(object)val == (Object)null))
			{
				Vector3 val2 = val.WorldToScreenPoint(egg.transform.position + Vector3.up * 0.35f);
				if (!(val2.z <= 0f))
				{
					eggWorldHud.Marker(guid, num, TeamColor(num), new Vector2(val2.x, val2.y));
				}
			}
		}

		private void InitializeLiveControls()
		{
			InitializeSharedTimer();
			ghostRadius = LiveFloat("Призрак", "Дальность полёта", 20f, 5f, 200f, "Метры от напарника. Изменения хост применяет в общем меню.");
			ghostSpeed = LiveFloat("Призрак", "Скорость полёта", 12f, 1f, 40f, "Скорость призрака.");
			ghostInterval = LiveFloat("Призрак", "Интервал выбора", 180f, 15f, 900f, "Общий интервал раундов: старый выбор заменяется новым у всех одновременно. Изменение интервала действует после следующей границы раунда.");
			ghostChoiceDuration = LiveFloat("Призрак", "Время на выбор действия", 20f, 5f, 120f, "Сколько секунд открыто окно выбора. Без выбора ничего не происходит. Новое значение действует со следующего раунда; окно не дольше интервала раундов.");
			campProtectionRadius = LiveFloat("Призрак", "Радиус достижения костра", 8f, 2f, 30f, "С этого расстояния до текущего костра игрок защищён от действий соперников до следующего биома.");
			ghostOpponents = ((BaseUnityPlugin)this).Config.Bind<bool>("Призрак", "Взаимодействие с соперниками", true, "Применение еды/лечения также на живых участников других пар. Не на наблюдателей.");
			ghostDynamite = ((BaseUnityPlugin)this).Config.Bind<bool>("Призрак", "Динамит", true, "Разрешить брать, зажигать и бросать динамит. Другие предметы бросать нельзя.");
			lanternRange = LiveFloat("Pitch Black", "Дальность фонаря", 30f, 5f, 80f, "Радиус света в метрах, в руках и на поясе. Только в Pitch Black.");
			lanternPower = LiveFloat("Pitch Black", "Яркость фонаря", 2.5f, 0.25f, 10f, "Множитель яркости от штатного фонаря. Только в Pitch Black.");
			Options = ConfigOptions();
		}

		private ConfigEntry<float> LiveFloat(string section, string key, float value, float min, float max, string description)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[0]));
		}

		private LiveOptions ConfigOptions()
		{
			LiveOptions liveOptions = new LiveOptions();
			liveOptions.Radius = ghostRadius.Value;
			liveOptions.Speed = ghostSpeed.Value;
			liveOptions.Interval = ghostInterval.Value;
			liveOptions.Opponents = ghostOpponents.Value;
			liveOptions.Dynamite = ghostDynamite.Value;
			liveOptions.LanternRange = lanternRange.Value;
			liveO

BepInEx/patchers/IBGameplay/IBGameplay.Bootstrap.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Mono.Cecil;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace IBGameplayStartup;

public static class Patcher
{
	public static IEnumerable<string> TargetDLLs => new string[0];

	public static bool Ready { get; private set; }

	public static void Patch(AssemblyDefinition assembly)
	{
	}

	public static void Finish()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		string text = FindController(Paths.PluginPath);
		if (text == null)
		{
			Logger.CreateLogSource("IBGameplay startup").LogInfo((object)"No active IBGameplay.dll under plugins; startup guards skipped.");
			return;
		}
		Harmony val = new Harmony("stasy.ibgameplay.startup");
		MethodInfo[] methods = typeof(Harmony).GetMethods(BindingFlags.Instance | BindingFlags.Public);
		foreach (MethodInfo methodInfo in methods)
		{
			if (methodInfo.Name == "PatchAll")
			{
				val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Patcher), "AllowPatchAll", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}
		Ready = true;
		Logger.CreateLogSource("IBGameplay startup").LogInfo((object)("WatchEgg and Chainbound automatic PatchAll parked; IBGameplay binds compatible rules. Original DLLs unchanged. Controller: " + text));
	}

	public static string FindController(string pluginRoot)
	{
		if (string.IsNullOrEmpty(pluginRoot) || !Directory.Exists(pluginRoot))
		{
			return null;
		}
		string[] files = Directory.GetFiles(pluginRoot, "IBGameplay.dll", SearchOption.AllDirectories);
		if (files.Length != 0)
		{
			return files[0];
		}
		return null;
	}

	private static bool AllowPatchAll(Harmony __instance)
	{
		return AllowOwner(__instance.Id);
	}

	public static bool AllowOwner(string owner)
	{
		if (owner != "sapphire009.watchegg" && owner != "Sapphire009.StaminaFull")
		{
			return owner != "patchnote.chainbound";
		}
		return false;
	}
}