Decompiled source of ReefClient v0.6.5

ReefClient.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ReefClient")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.6.5.0")]
[assembly: AssemblyInformationalVersion("0.6.5+1610e87379a7e5606a8e54743b3a1516f4c67255")]
[assembly: AssemblyProduct("ReefClient")]
[assembly: AssemblyTitle("ReefClient")]
[assembly: AssemblyVersion("0.6.5.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ReefClient
{
	internal static class Deposit
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyboardShortcut> Key;

		internal static ConfigEntry<float> Range;

		internal static ConfigEntry<bool> AnyChest;

		internal static ConfigEntry<int> FirstRow;

		private static bool wasOpen;

		private static readonly List<Container> cache = new List<Container>();

		private static readonly MethodInfo checkAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);

		private static readonly FieldRef<Container, bool> checkGuardStone = AccessTools.FieldRefAccess<Container, bool>("m_checkGuardStone");

		private static int keptInSlots;

		private static MethodInfo eaqsIsItemInSlot;

		private static bool eaqsLooked;

		private static bool PrivacyAllows(Container c, long pid)
		{
			if (checkAccess == null)
			{
				return true;
			}
			return (bool)checkAccess.Invoke(c, new object[1] { pid });
		}

		internal static void Bind(ConfigFile cfg)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			Enabled = cfg.Bind<bool>("Deposit", "Enabled", true, "Quick deposit: with the inventory open, press the key to move the rows from 'First row' down into nearby chests (only chests already holding the item, unless 'Any chest').");
			Key = cfg.Bind<KeyboardShortcut>("Deposit", "Key", new KeyboardShortcut((KeyCode)114, Array.Empty<KeyCode>()), "Key pressed while the inventory is open.");
			Range = cfg.Bind<float>("Deposit", "Range", 20f, new ConfigDescription("Radius in metres around you (chests are listed when the inventory opens).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>()));
			AnyChest = cfg.Bind<bool>("Deposit", "Any chest", false, "true = what no chest already holds goes to the nearest chest with a free slot. The Reef: false — only matching chests receive, the rest stays with you.");
			FirstRow = cfg.Bind<int>("Deposit", "First row", 3, new ConfigDescription("Rows from this one down are deposited (1 = hotbar, 4 = last row of the vanilla grid); rows above stay with you. The Reef: 3 = rows 3 and 4 go, hotbar and row 2 stay.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 4), Array.Empty<object>()));
		}

		internal static void Tick()
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if (Enabled == null || !Enabled.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)InventoryGui.instance == (Object)null)
			{
				wasOpen = false;
				return;
			}
			bool flag = InventoryGui.IsVisible();
			if (flag && !wasOpen)
			{
				try
				{
					Scan(localPlayer);
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("deposit scan: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
			wasOpen = flag;
			if (!flag || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || Console.IsVisible())
			{
				return;
			}
			KeyboardShortcut value = Key.Value;
			if (!((KeyboardShortcut)(ref value)).IsDown())
			{
				return;
			}
			try
			{
				Run(localPlayer);
			}
			catch (Exception ex2)
			{
				ReefClientPlugin.Log.LogWarning((object)("deposit: " + ex2.GetType().Name + ": " + ex2.Message));
			}
		}

		private static void Scan(Player player)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			cache.Clear();
			Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, Range.Value);
			for (int i = 0; i < array.Length; i++)
			{
				Container componentInParent = ((Component)array[i]).GetComponentInParent<Container>();
				if (!((Object)(object)componentInParent == (Object)null) && !cache.Contains(componentInParent) && !((Object)(object)((Component)componentInParent).GetComponentInParent<Character>() != (Object)null))
				{
					ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>();
					if (!((Object)(object)component == (Object)null) && component.IsValid())
					{
						cache.Add(componentInParent);
					}
				}
			}
		}

		private static bool Eligible(ItemData it, Player player)
		{
			if (it == null || it.m_equipped || ((Humanoid)player).IsItemEquiped(it))
			{
				return false;
			}
			if (it.m_gridPos.y < FirstRow.Value - 1)
			{
				return false;
			}
			if (it.m_customData != null && it.m_customData.ContainsKey("eaqs_slot"))
			{
				return false;
			}
			if (InEaqsSlot(it))
			{
				keptInSlots++;
				return false;
			}
			return true;
		}

		private static bool InEaqsSlot(ItemData it)
		{
			if (!eaqsLooked)
			{
				eaqsLooked = true;
				try
				{
					Type type = Type.GetType("EquipmentAndQuickSlots.Slots, EquipmentAndQuickSlots");
					eaqsIsItemInSlot = ((type == null) ? null : type.GetMethod("IsItemInSlot", new Type[1] { typeof(ItemData) }));
					ReefClientPlugin.Log.LogInfo((object)("deposit: EAQS " + ((eaqsIsItemInSlot == null) ? "not found (no slot filter)" : "Slots.IsItemInSlot bound")));
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("deposit: EAQS lookup failed: " + ex.Message));
				}
			}
			if (eaqsIsItemInSlot == null)
			{
				return false;
			}
			try
			{
				return (bool)eaqsIsItemInSlot.Invoke(null, new object[1] { it });
			}
			catch
			{
				return false;
			}
		}

		private static Inventory Usable(Container c, long pid, ref int skipped)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			ZDO zDO = component.GetZDO();
			if (c.IsInUse() || (zDO != null && zDO.GetInt(ZDOVars.s_inUse, 0) == 1))
			{
				skipped++;
				return null;
			}
			if (checkGuardStone.Invoke(c) && !PrivateArea.CheckAccess(((Component)c).transform.position, 0f, false, false))
			{
				skipped++;
				return null;
			}
			if (!PrivacyAllows(c, pid))
			{
				skipped++;
				return null;
			}
			Inventory inventory = c.GetInventory();
			if (inventory == null)
			{
				return null;
			}
			if (!component.IsOwner())
			{
				component.ClaimOwnership();
			}
			if (!component.IsOwner())
			{
				skipped++;
				ReefClientPlugin.Log.LogInfo((object)("deposit: could not own " + ((Object)c).name + " — skipped"));
				return null;
			}
			return inventory;
		}

		private static int Move(Inventory inv, Inventory target, ItemData it)
		{
			int stack = it.m_stack;
			int num = target.FindFreeStackSpace(it.m_shared.m_name, (float)it.m_worldLevel);
			if (num >= stack || target.HaveEmptySlot())
			{
				if (target.AddItem(it))
				{
					inv.RemoveItem(it);
					return stack;
				}
				if (it.m_stack < stack)
				{
					return stack - it.m_stack;
				}
				return 0;
			}
			if (num > 0)
			{
				ItemData val = it.Clone();
				val.m_stack = num;
				if (target.AddItem(val))
				{
					inv.RemoveItem(it, num);
					return num;
				}
			}
			return 0;
		}

		private static void Run(Player player)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			cache.RemoveAll((Container c) => (Object)(object)c == (Object)null || (Object)(object)((Component)c).GetComponent<ZNetView>() == (Object)null || !((Component)c).GetComponent<ZNetView>().IsValid());
			Inventory inventory = ((Humanoid)player).GetInventory();
			long playerID = player.GetPlayerID();
			int num = 0;
			int skipped = 0;
			keptInSlots = 0;
			HashSet<Container> hashSet = new HashSet<Container>();
			Vector3 pos = ((Component)player).transform.position;
			List<Container> list = new List<Container>(cache);
			list.Sort((Container a, Container b) => Vector3.Distance(((Component)a).transform.position, pos).CompareTo(Vector3.Distance(((Component)b).transform.position, pos)));
			foreach (Container item in list)
			{
				bool flag = false;
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (Eligible(allItem, player) && item.GetInventory() != null && item.GetInventory().ContainsItemByName(allItem.m_shared.m_name))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					continue;
				}
				Inventory val = Usable(item, playerID, ref skipped);
				if (val == null)
				{
					continue;
				}
				int num2 = 0;
				foreach (ItemData item2 in new List<ItemData>(inventory.GetAllItems()))
				{
					if (Eligible(item2, player) && val.ContainsItemByName(item2.m_shared.m_name))
					{
						num2 += Move(inventory, val, item2);
					}
				}
				if (num2 > 0)
				{
					hashSet.Add(item);
					num += num2;
				}
			}
			List<ItemData> list2 = new List<ItemData>();
			if (AnyChest.Value)
			{
				foreach (ItemData allItem2 in inventory.GetAllItems())
				{
					if (Eligible(allItem2, player))
					{
						list2.Add(allItem2);
					}
				}
			}
			foreach (Container item3 in list)
			{
				if (list2.Count == 0)
				{
					break;
				}
				Inventory val2 = Usable(item3, playerID, ref skipped);
				if (val2 == null)
				{
					continue;
				}
				int num3 = 0;
				foreach (ItemData item4 in new List<ItemData>(list2))
				{
					if (val2.HaveEmptySlot() || val2.FindFreeStackSpace(item4.m_shared.m_name, (float)item4.m_worldLevel) > 0)
					{
						int num4 = Move(inventory, val2, item4);
						num3 += num4;
						if (num4 > 0 && !inventory.ContainsItem(item4))
						{
							list2.Remove(item4);
						}
					}
				}
				if (num3 > 0)
				{
					hashSet.Add(item3);
					num += num3;
				}
			}
			int count = hashSet.Count;
			if (num > 0)
			{
				((Character)player).Message((MessageType)2, num + " items deposited into " + count + ((count == 1) ? " chest" : " chests"), 0, (Sprite)null, false);
			}
			else
			{
				((Character)player).Message((MessageType)2, (cache.Count == 0) ? "No chest in range" : "Nothing to deposit", 0, (Sprite)null, false);
			}
			ReefClientPlugin.Log.LogInfo((object)("deposit: " + num + " items → " + count + " chest(s), " + skipped + " skipped, " + cache.Count + " in range, " + list2.Count + " stack(s) left, " + keptInSlots + " kept in slots (grid " + inventory.GetWidth() + "x" + inventory.GetHeight() + ")"));
		}
	}
	internal static class FavorHud
	{
		private static int count = -1;

		private static GameObject root;

		private static Image icon;

		private static TMP_Text label;

		private static Sprite sprite;

		private static bool spriteTried;

		private static float builtSize = -1f;

		internal static bool EverReceived => count >= 0;

		internal static int Count => count;

		internal static void Set(int n)
		{
			count = Math.Max(0, n);
			if ((Object)(object)label != (Object)null)
			{
				label.text = count.ToString();
			}
		}

		internal static void Tick()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_smallRoot == (Object)null)
			{
				root = null;
				icon = null;
				label = null;
				return;
			}
			if ((Object)(object)root == (Object)null || builtSize != ReefClientPlugin.HudSize.Value)
			{
				try
				{
					Build(instance);
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("favor hud: " + ex.GetType().Name + ": " + ex.Message));
					return;
				}
			}
			bool flag = EverReceived || !ReefClientPlugin.HiddenUntilFirstFavor.Value;
			if (root.activeSelf != flag)
			{
				root.SetActive(flag);
			}
		}

		private static void Build(Minimap mm)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_008a: 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_00b4: 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)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: 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)
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root);
			}
			float num = Mathf.Clamp(ReefClientPlugin.HudSize.Value, 8f, 128f);
			builtSize = ReefClientPlugin.HudSize.Value;
			root = new GameObject("ReefFavors", new Type[1] { typeof(RectTransform) });
			RectTransform component = root.GetComponent<RectTransform>();
			((Transform)component).SetParent(mm.m_smallRoot.transform, false);
			component.anchorMin = new Vector2(0.5f, 0f);
			component.anchorMax = new Vector2(0.5f, 0f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = new Vector2(ReefClientPlugin.HudX.Value, ReefClientPlugin.HudY.Value);
			component.sizeDelta = new Vector2(num * 3f, num);
			GameObject val = new GameObject("Icon", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			RectTransform component2 = val.GetComponent<RectTransform>();
			((Transform)component2).SetParent((Transform)(object)component, false);
			component2.anchorMin = new Vector2(0f, 0.5f);
			component2.anchorMax = new Vector2(0f, 0.5f);
			component2.pivot = new Vector2(0f, 0.5f);
			component2.anchoredPosition = new Vector2(num * 0.5f, 0f);
			component2.sizeDelta = new Vector2(num, num);
			icon = val.GetComponent<Image>();
			((Graphic)icon).raycastTarget = false;
			icon.preserveAspect = true;
			Sprite val2 = LoadSprite();
			if ((Object)(object)val2 != (Object)null)
			{
				icon.sprite = val2;
			}
			else
			{
				((Graphic)icon).color = new Color(0.95f, 0.8f, 0.35f, 0.9f);
			}
			GameObject val3 = new GameObject("Count", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(TextMeshProUGUI)
			});
			RectTransform component3 = val3.GetComponent<RectTransform>();
			((Transform)component3).SetParent((Transform)(object)component, false);
			component3.anchorMin = new Vector2(0f, 0f);
			component3.anchorMax = new Vector2(1f, 1f);
			component3.pivot = new Vector2(0f, 0.5f);
			component3.offsetMin = new Vector2(num * 1.65f, 0f);
			component3.offsetMax = new Vector2(0f, 0f);
			label = (TMP_Text)(object)val3.GetComponent<TextMeshProUGUI>();
			TMP_Text biomeNameSmall = mm.m_biomeNameSmall;
			if ((Object)(object)biomeNameSmall != (Object)null)
			{
				label.font = biomeNameSmall.font;
				label.fontSharedMaterial = biomeNameSmall.fontSharedMaterial;
				((Graphic)label).color = ((Graphic)biomeNameSmall).color;
			}
			else
			{
				((Graphic)label).color = new Color(0.95f, 0.85f, 0.5f, 1f);
			}
			label.fontSize = num * 0.85f;
			label.alignment = (TextAlignmentOptions)4097;
			label.textWrappingMode = (TextWrappingModes)0;
			((Graphic)label).raycastTarget = false;
			label.text = ((count >= 0) ? count.ToString() : "");
			ReefClientPlugin.Log.LogInfo((object)("favor hud built under the minimap (size " + num + ", offset " + ReefClientPlugin.HudX.Value + "," + ReefClientPlugin.HudY.Value + ", sprite " + (((Object)(object)val2 != (Object)null) ? "eye.png" : "fallback") + ")"));
		}

		private static Sprite LoadSprite()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sprite != (Object)null || spriteTried)
			{
				return sprite;
			}
			spriteTried = true;
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ReefClient.Resources.eye.png");
				if (stream == null)
				{
					ReefClientPlugin.Log.LogWarning((object)"favor hud: embedded eye.png not found");
					return null;
				}
				byte[] array = new byte[stream.Length];
				int num;
				for (int i = 0; i < array.Length; i += num)
				{
					num = stream.Read(array, i, array.Length - i);
					if (num <= 0)
					{
						break;
					}
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				MethodInfo method = typeof(ImageConversion).GetMethod("LoadImage", new Type[2]
				{
					typeof(Texture2D),
					typeof(byte[])
				});
				if (method == null)
				{
					ReefClientPlugin.Log.LogWarning((object)"favor hud: ImageConversion.LoadImage(Texture2D, byte[]) not found");
					return null;
				}
				if (!(bool)method.Invoke(null, new object[2] { val, array }))
				{
					ReefClientPlugin.Log.LogWarning((object)"favor hud: eye.png did not decode");
					return null;
				}
				((Texture)val).filterMode = (FilterMode)1;
				sprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
				return sprite;
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("favor hud sprite: " + ex.GetType().Name + ": " + ex.Message));
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(ZNetView), "InvokeRPC", new Type[]
	{
		typeof(string),
		typeof(object[])
	})]
	internal static class FermenterPull
	{
		private static int fixedCount;

		private static void Prefix(ZNetView __instance, string method, ref object[] parameters)
		{
			try
			{
				if (method != "RPC_AddItem" || parameters == null || parameters.Length == 0 || !(parameters[0] is string text) || (Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).GetComponent<Fermenter>() == (Object)null)
				{
					return;
				}
				bool flag = default(bool);
				int num;
				if (parameters.Length > 1)
				{
					object obj = parameters[1];
					if (obj is bool)
					{
						flag = (bool)obj;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				bool flag2 = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
				parameters = new object[2]
				{
					StringExtensionMethods.GetStableHashCode(text),
					flag2
				};
				fixedCount++;
				if (fixedCount <= 3 || fixedCount % 50 == 0)
				{
					ReefClientPlugin.Log.LogInfo((object)("fermenter pull: RPC_AddItem(" + text + ") rewritten to the 1.0 shape (hash + cheated) [" + fixedCount + "]"));
				}
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("fermenter pull: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	[BepInPlugin("reef.client", "Reef Client", "0.6.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class ReefClientPlugin : BaseUnityPlugin
	{
		public const string GUID = "reef.client";

		public const string NAME = "Reef Client";

		public const string VERSION = "0.6.5";

		public const string BUILD = "reef";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> HudX;

		internal static ConfigEntry<float> HudY;

		internal static ConfigEntry<float> HudSize;

		internal static ConfigEntry<bool> HiddenUntilFirstFavor;

		internal static ConfigEntry<float> MaxEnvMinutes;

		internal static ConfigEntry<bool> SendChatCopy;

		private Harmony harmony;

		private void Awake()
		{
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			HudX = ((BaseUnityPlugin)this).Config.Bind<float>("Favors", "HudX", 0f, "Horizontal offset (px) of the favor counter from the bottom-centre of the small minimap.");
			HudY = ((BaseUnityPlugin)this).Config.Bind<float>("Favors", "HudY", -6f, "Vertical offset (px) of the favor counter from the bottom edge of the small minimap (negative = below).");
			HudSize = ((BaseUnityPlugin)this).Config.Bind<float>("Favors", "HudSize", 28f, "Icon height in px; the number scales with it.");
			HiddenUntilFirstFavor = ((BaseUnityPlugin)this).Config.Bind<bool>("Favors", "HiddenUntilFirstFavor", true, "true = nothing is drawn until the server has sent a favor count at least once this game session.");
			MaxEnvMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Weather", "MaxEnvMinutes", 60f, "Safety: a forced sky (Reef_SetEnv) clears itself after this many real minutes even if the server never sends the reset. 0 = never.");
			SendChatCopy = ((BaseUnityPlugin)this).Config.Bind<bool>("Chat", "SendCopyToServer", true, "Send a copy of every chat line you type to the server (Reef_Chat) so the director hears you even when you play alone. Chat is never READ by this plugin.");
			Deposit.Bind(((BaseUnityPlugin)this).Config);
			ReefFood.Bind(((BaseUnityPlugin)this).Config);
			ReefTorches.Bind(((BaseUnityPlugin)this).Config);
			ReefStumps.Bind(((BaseUnityPlugin)this).Config);
			ReefFarming.Bind(((BaseUnityPlugin)this).Config);
			ReefHarvest.Bind(((BaseUnityPlugin)this).Config);
			ReefPortals.Bind(((BaseUnityPlugin)this).Config);
			ReefForgeGate.Bind(((BaseUnityPlugin)this).Config);
			ReefClock.Bind(((BaseUnityPlugin)this).Config);
			ReefAdrenaline.Bind(((BaseUnityPlugin)this).Config);
			ReefRevive.Bind(((BaseUnityPlugin)this).Config);
			ReefFenrir.Bind(((BaseUnityPlugin)this).Config);
			ReefSeedBed.Bind(((BaseUnityPlugin)this).Config);
			ReefCompost.Bind(((BaseUnityPlugin)this).Config);
			harmony = new Harmony("reef.client");
			harmony.PatchAll(typeof(ZNetAwakePatch));
			harmony.PatchAll(typeof(ChatSendPatch));
			harmony.PatchAll(typeof(FermenterPull));
			harmony.PatchAll(typeof(ReefFood.GetTotalFoodValuePatch));
			harmony.PatchAll(typeof(ReefTorches.UpdateFireplacePatch));
			harmony.PatchAll(typeof(ReefStumps.DamagePatch));
			harmony.PatchAll(typeof(ReefStumps.DropPatch));
			harmony.PatchAll(typeof(ReefHarvest.InteractPatch));
			harmony.PatchAll(typeof(ReefPortals.UpdatePortalPatch));
			harmony.PatchAll(typeof(ReefForgeGate.InteractPatch));
			harmony.PatchAll(typeof(ReefAdrenaline.AwakePatch));
			harmony.PatchAll(typeof(ReefRevive.OnDeathPatch));
			harmony.PatchAll(typeof(ReefRevive.AwakeRegisterPatch));
			harmony.PatchAll(typeof(ReefRevive.HoverTextPatch));
			harmony.PatchAll(typeof(ReefRevive.GuardianPowerPatch));
			harmony.PatchAll(typeof(ReefRevive.OnSpawnedPatch));
			harmony.PatchAll(typeof(ReefFenrir.ObjectDBAwakePatch));
			harmony.PatchAll(typeof(ReefFenrir.ObjectDBCopyPatch));
			harmony.PatchAll(typeof(ReefFenrir.OnSpawnedPatch));
			harmony.PatchAll(typeof(ReefCompost.MessagePatch));
			ReefSeedBed.Patch(harmony);
			Log.LogInfo((object)"Reef Client 0.6.5 loaded (build: reef)");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		private void Update()
		{
			ReefFarming.TickOnce();
			Receivers.Tick();
			FavorHud.Tick();
			ReefClock.Tick();
			ReefRevive.Tick();
			Deposit.Tick();
		}
	}
	[HarmonyPatch(typeof(ZNet), "Awake")]
	internal static class ZNetAwakePatch
	{
		private static void Postfix(ZNet __instance)
		{
			try
			{
				if (__instance.IsDedicated())
				{
					ReefClientPlugin.Log.LogInfo((object)"dedicated server: ReefClient registers nothing");
					return;
				}
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance == null)
				{
					ReefClientPlugin.Log.LogWarning((object)"ZRoutedRpc.instance is null after ZNet.Awake — receivers not registered");
					return;
				}
				instance.Register<int>("Reef_FavorCount", (Action<long, int>)Receivers.OnFavorCount);
				instance.Register<string>("Reef_SetEnv", (Action<long, string>)Receivers.OnSetEnv);
				instance.Register<float, float, float>("Reef_SetWind", (Action<long, float, float, float>)Receivers.OnSetWind);
				ReefClientPlugin.Log.LogInfo((object)"receivers registered: Reef_FavorCount, Reef_SetEnv, Reef_SetWind");
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("register: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Chat), "SendText", new Type[]
	{
		typeof(Type),
		typeof(string)
	})]
	internal static class ChatSendPatch
	{
		private static void Postfix(Type type, string text)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected I4, but got Unknown
			try
			{
				if (ReefClientPlugin.SendChatCopy.Value && (int)type != 3 && !string.IsNullOrEmpty(text))
				{
					ZNet instance = ZNet.instance;
					if (!((Object)(object)instance == (Object)null) && !instance.IsServer() && ZRoutedRpc.instance != null)
					{
						ZRoutedRpc.instance.InvokeRoutedRPC("Reef_Chat", new object[2]
						{
							(int)type,
							text
						});
					}
				}
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("Reef_Chat: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	internal static class Receivers
	{
		private static float windDeadline = -1f;

		private static float envDeadline = -1f;

		private static string heldEnv = "";

		internal static void OnFavorCount(long sender, int n)
		{
			ReefClientPlugin.Log.LogInfo((object)("Reef_FavorCount " + n + " (from " + sender + ")"));
			FavorHud.Set(n);
		}

		internal static void OnSetEnv(long sender, string name)
		{
			name = name ?? "";
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null)
			{
				ReefClientPlugin.Log.LogWarning((object)("Reef_SetEnv '" + name + "': no EnvMan"));
				return;
			}
			instance.m_debugEnv = name;
			heldEnv = name;
			float value = ReefClientPlugin.MaxEnvMinutes.Value;
			envDeadline = ((name.Length > 0 && value > 0f) ? (Time.realtimeSinceStartup + value * 60f) : (-1f));
			ReefClientPlugin.Log.LogInfo((object)("Reef_SetEnv '" + name + "' (from " + sender + ")" + ((name.Length == 0) ? " → natural sky" : "")));
		}

		internal static void OnSetWind(long sender, float angle, float intensity, float minutes)
		{
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null)
			{
				ReefClientPlugin.Log.LogWarning((object)"Reef_SetWind: no EnvMan");
				return;
			}
			if (minutes <= 0f)
			{
				instance.ResetDebugWind();
				windDeadline = -1f;
				ReefClientPlugin.Log.LogInfo((object)("Reef_SetWind reset (from " + sender + ")"));
				return;
			}
			instance.SetDebugWind(angle, intensity);
			windDeadline = Time.realtimeSinceStartup + Mathf.Min(minutes, 1440f) * 60f;
			ReefClientPlugin.Log.LogInfo((object)("Reef_SetWind angle=" + angle + " intensity=" + intensity + " for " + minutes + " min (from " + sender + ")"));
		}

		internal static void Tick()
		{
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (windDeadline > 0f && realtimeSinceStartup >= windDeadline)
			{
				windDeadline = -1f;
				instance.ResetDebugWind();
				ReefClientPlugin.Log.LogInfo((object)"wind hold expired → natural wind");
			}
			if (envDeadline > 0f && realtimeSinceStartup >= envDeadline)
			{
				envDeadline = -1f;
				if (instance.m_debugEnv == heldEnv)
				{
					instance.m_debugEnv = "";
				}
				ReefClientPlugin.Log.LogInfo((object)"sky hold expired (MaxEnvMinutes) → natural sky");
			}
		}
	}
	internal static class ReefAdrenaline
	{
		[HarmonyPatch(typeof(Player), "Awake")]
		internal static class AwakePatch
		{
			private static void Postfix(Player __instance)
			{
				try
				{
					if (Enabled.Value)
					{
						if (!loggedOriginal)
						{
							loggedOriginal = true;
							ReefClientPlugin.Log.LogInfo((object)("adrenaline: prefab decay curve " + Describe(__instance.m_adrenalineDegen) + " ; delay curve " + Describe(__instance.m_adrenalineDegenDelay) + " ; max " + __instance.m_maxAdrenaline));
						}
						float num = Mathf.Clamp(DrainEmpty.Value, 0f, 100f);
						float num2 = Mathf.Clamp(DrainFull.Value, 0f, 100f);
						float num3 = Mathf.Clamp(PauseEmpty.Value, 0f, 600f);
						float num4 = Mathf.Clamp(PauseFull.Value, 0f, 600f);
						__instance.m_adrenalineDegen = AnimationCurve.Linear(0f, num, 1f, num2);
						__instance.m_adrenalineDegenDelay = AnimationCurve.Linear(0f, num3, 1f, num4);
						ReefClientPlugin.Log.LogInfo((object)("adrenaline: drain " + num + "→" + num2 + " /s, pause " + num3 + "→" + num4 + " s (empty→full)"));
					}
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("adrenaline: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> DrainEmpty;

		internal static ConfigEntry<float> DrainFull;

		internal static ConfigEntry<float> PauseEmpty;

		internal static ConfigEntry<float> PauseFull;

		internal static ConfigEntry<int> ConfigVersion;

		private const int CurrentVersion = 2;

		private static bool loggedOriginal;

		internal static void Bind(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("Adrenaline", "Enabled", true, "Change how the adrenaline gauge drains once you stop fighting. Vanilla: 1 to 4 points per second after a 10 to 6 s pause.");
			DrainEmpty = cfg.Bind<float>("Adrenaline", "DrainPerSecondWhenEmpty", 0.3f, "Points lost per second when the gauge is nearly empty. Vanilla 1.");
			DrainFull = cfg.Bind<float>("Adrenaline", "DrainPerSecondWhenFull", 1f, "Points lost per second when the gauge is full. Vanilla 4.");
			PauseEmpty = cfg.Bind<float>("Adrenaline", "PauseSecondsWhenEmpty", 30f, "Seconds without a gain before the gauge starts draining, when it is nearly empty. Vanilla 10.");
			PauseFull = cfg.Bind<float>("Adrenaline", "PauseSecondsWhenFull", 18f, "Seconds without a gain before the gauge starts draining, when it is full. Vanilla 6.");
			ConfigVersion = cfg.Bind<int>("Adrenaline", "ConfigVersion", 1, "Do not edit. Records which set of Reef values this section holds. A section older than the current set is rewritten once at startup, so a change of realm numbers reaches a config file that was already written.");
			Migrate();
		}

		private static void Migrate()
		{
			try
			{
				int value = ConfigVersion.Value;
				if (value < 2)
				{
					string text = Show();
					DrainEmpty.Value = (float)((ConfigEntryBase)DrainEmpty).DefaultValue;
					DrainFull.Value = (float)((ConfigEntryBase)DrainFull).DefaultValue;
					PauseEmpty.Value = (float)((ConfigEntryBase)PauseEmpty).DefaultValue;
					PauseFull.Value = (float)((ConfigEntryBase)PauseFull).DefaultValue;
					ConfigVersion.Value = 2;
					ReefClientPlugin.Log.LogInfo((object)("adrenaline: config v" + value + " -> v" + 2 + " (was " + text + ", now " + Show() + ")"));
				}
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("adrenaline migrate: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static string Show()
		{
			return "drain " + DrainEmpty.Value + "/" + DrainFull.Value + " per s, pause " + PauseEmpty.Value + "/" + PauseFull.Value + " s";
		}

		private static string Describe(AnimationCurve curve)
		{
			if (curve == null)
			{
				return "null";
			}
			Keyframe[] keys = curve.keys;
			string[] array = new string[keys.Length];
			for (int i = 0; i < keys.Length; i++)
			{
				array[i] = ((Keyframe)(ref keys[i])).time.ToString("0.##") + "→" + ((Keyframe)(ref keys[i])).value.ToString("0.##");
			}
			return "[" + string.Join(", ", array) + "]";
		}
	}
	internal static class ReefClock
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> ShowCountdown;

		internal static ConfigEntry<float> X;

		internal static ConfigEntry<float> Y;

		internal static ConfigEntry<float> Size;

		private const float NightStart = 0.85f;

		private const float MorningStart = 0.15f;

		private static GameObject root;

		private static TMP_Text label;

		private static float builtSize = -1f;

		private static float nextRefresh;

		private static bool loggedDayLength;

		internal static void Bind(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("Clock", "Enabled", true, "Show a small day/night clock under the minimap.");
			ShowCountdown = cfg.Bind<bool>("Clock", "ShowCountdown", true, "Append how long until night falls (or until dawn, at night).");
			X = cfg.Bind<float>("Clock", "HudX", 0f, "Horizontal offset (px) from the bottom-centre of the small minimap.");
			Y = cfg.Bind<float>("Clock", "HudY", -38f, "Vertical offset (px) from the bottom edge of the small minimap (negative = below). Default sits just under the favor counter.");
			Size = cfg.Bind<float>("Clock", "HudSize", 20f, "Text height in px.");
		}

		internal static void Tick()
		{
			if (!Enabled.Value)
			{
				if ((Object)(object)root != (Object)null && root.activeSelf)
				{
					root.SetActive(false);
				}
				return;
			}
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_smallRoot == (Object)null)
			{
				root = null;
				label = null;
				return;
			}
			if ((Object)(object)root == (Object)null || builtSize != Size.Value)
			{
				try
				{
					Build(instance);
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("clock: " + ex.GetType().Name + ": " + ex.Message));
					return;
				}
			}
			if (!root.activeSelf)
			{
				root.SetActive(true);
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < nextRefresh)
			{
				return;
			}
			nextRefresh = realtimeSinceStartup + 0.5f;
			try
			{
				label.text = Compose();
			}
			catch (Exception ex2)
			{
				label.text = "";
				ReefClientPlugin.Log.LogWarning((object)("clock text: " + ex2.GetType().Name + ": " + ex2.Message));
			}
		}

		internal static string Compose()
		{
			EnvMan instance = EnvMan.instance;
			ZNet instance2 = ZNet.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null)
			{
				return "";
			}
			double num = instance.m_dayLengthSec;
			if (num <= 0.0)
			{
				return "";
			}
			if (!loggedDayLength)
			{
				loggedDayLength = true;
				ReefClientPlugin.Log.LogInfo((object)("clock: day length " + num + " s (prefab value)"));
			}
			double timeSeconds = instance2.GetTimeSeconds();
			int num2 = (int)(timeSeconds / num);
			double num3 = timeSeconds % num / num;
			int num4 = (int)(Mathf.Repeat(instance.GetDayFraction(), 1f) * 24f * 60f);
			string text = (num4 / 60).ToString("00") + ":" + (num4 % 60).ToString("00");
			string text2 = "Day " + num2 + " · " + text;
			if (!ShowCountdown.Value)
			{
				return text2;
			}
			double seconds;
			string text3;
			if (num3 >= 0.8500000238418579 || num3 < 0.15000000596046448)
			{
				seconds = (((num3 >= 0.8500000238418579) ? 1.1500000059604645 : 0.15000000596046448) - num3) * num;
				text3 = "dawn";
			}
			else
			{
				seconds = (0.8500000238418579 - num3) * num;
				text3 = "night";
			}
			return text2 + " · " + text3 + " in " + Span(seconds);
		}

		private static string Span(double seconds)
		{
			if (seconds < 60.0)
			{
				return Math.Max(1, (int)Math.Ceiling(seconds)) + " s";
			}
			return (int)Math.Ceiling(seconds / 60.0) + " min";
		}

		private static void Build(Minimap mm)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_008a: 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_00b4: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root);
			}
			float num = Mathf.Clamp(Size.Value, 8f, 64f);
			builtSize = Size.Value;
			root = new GameObject("ReefClock", new Type[1] { typeof(RectTransform) });
			RectTransform component = root.GetComponent<RectTransform>();
			((Transform)component).SetParent(mm.m_smallRoot.transform, false);
			component.anchorMin = new Vector2(0.5f, 0f);
			component.anchorMax = new Vector2(0.5f, 0f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = new Vector2(X.Value, Y.Value);
			component.sizeDelta = new Vector2(num * 14f, num * 1.2f);
			GameObject val = new GameObject("Text", new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(TextMeshProUGUI)
			});
			RectTransform component2 = val.GetComponent<RectTransform>();
			((Transform)component2).SetParent((Transform)(object)component, false);
			component2.anchorMin = Vector2.zero;
			component2.anchorMax = Vector2.one;
			component2.offsetMin = Vector2.zero;
			component2.offsetMax = Vector2.zero;
			label = (TMP_Text)(object)val.GetComponent<TextMeshProUGUI>();
			TMP_Text biomeNameSmall = mm.m_biomeNameSmall;
			if ((Object)(object)biomeNameSmall != (Object)null)
			{
				label.font = biomeNameSmall.font;
				label.fontSharedMaterial = biomeNameSmall.fontSharedMaterial;
				((Graphic)label).color = ((Graphic)biomeNameSmall).color;
			}
			else
			{
				((Graphic)label).color = new Color(0.95f, 0.85f, 0.5f, 1f);
			}
			label.fontSize = num * 0.85f;
			label.alignment = (TextAlignmentOptions)4098;
			label.textWrappingMode = (TextWrappingModes)0;
			((Graphic)label).raycastTarget = false;
			label.text = "";
			nextRefresh = 0f;
			ReefClientPlugin.Log.LogInfo((object)("clock built under the minimap (size " + num + ", offset " + X.Value + "," + Y.Value + ")"));
		}
	}
	internal static class ReefCompost
	{
		[HarmonyPatch(typeof(Player), "Message", new Type[]
		{
			typeof(MessageType),
			typeof(string),
			typeof(int),
			typeof(Sprite),
			typeof(bool)
		})]
		internal static class MessagePatch
		{
			private static void Prefix(ref string msg)
			{
				try
				{
					if (Enabled.Value && !(msg != "Item Deleted"))
					{
						msg = "Item Composted";
						if (!loggedOnce)
						{
							loggedOnce = true;
							ReefClientPlugin.Log.LogInfo((object)"compost: \"Item Deleted\" shown as \"Item Composted\"");
						}
					}
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("compost: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		internal static ConfigEntry<bool> Enabled;

		private const string Deleted = "Item Deleted";

		private const string Composted = "Item Composted";

		private static bool loggedOnce;

		internal static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("Compost", "Enabled", true, "true = RecyclePlus's \"Item Deleted\" message reads \"Item Composted\" (the realm's trash button is the compost). false = the mod's own text.");
		}
	}
	internal static class ReefFarming
	{
		private const string PlantEasilyGuid = "advize.PlantEasily";

		internal static ConfigEntry<bool> StandDownPlantEasily;

		private static bool done;

		internal static void Bind(ConfigFile config)
		{
			StandDownPlantEasily = config.Bind<bool>("Farming", "StandDownPlantEasily", true, "true = if Advize-PlantEasily is still installed, switch it off at startup ([General] ModActive). The realm plants vanilla and harvests through Reef Client instead. Press F8 in game to wake it for your session if you really want its grid back.");
		}

		internal static void TickOnce()
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			if (done)
			{
				return;
			}
			done = true;
			try
			{
				if (StandDownPlantEasily != null && StandDownPlantEasily.Value)
				{
					if (!Chainloader.PluginInfos.TryGetValue("advize.PlantEasily", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null)
					{
						ReefClientPlugin.Log.LogInfo((object)"farming: PlantEasily not installed — vanilla planting, Reef harvest");
						return;
					}
					ConfigEntry<bool> val = default(ConfigEntry<bool>);
					if (!value.Instance.Config.TryGetEntry<bool>(new ConfigDefinition("General", "ModActive"), ref val))
					{
						ReefClientPlugin.Log.LogWarning((object)"farming: PlantEasily has no [General] ModActive any more — not switching it off (the mod was reorganised; this file needs a look)");
						return;
					}
					if (!val.Value)
					{
						ReefClientPlugin.Log.LogInfo((object)"farming: PlantEasily already off");
						return;
					}
					val.Value = false;
					ReefClientPlugin.Log.LogInfo((object)"farming: PlantEasily switched off (ModActive=false) — vanilla planting, Reef harvest. F8 wakes it for this session if you want its grid back.");
				}
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("farming: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	internal static class ReefFenrir
	{
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		internal static class ObjectDBAwakePatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(ObjectDB __instance)
			{
				try
				{
					Apply(__instance, "ObjectDB.Awake");
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("fenrir: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		internal static class ObjectDBCopyPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(ObjectDB __instance)
			{
				try
				{
					Apply(__instance, "ObjectDB.CopyOtherDB");
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("fenrir: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnSpawned", new Type[] { typeof(bool) })]
		internal static class OnSpawnedPatch
		{
			private static void Postfix(Player __instance)
			{
				try
				{
					if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
					{
						Apply(ObjectDB.instance, "spawn");
					}
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("fenrir: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		internal static ConfigEntry<bool> Enabled;

		private const float BodyPiece = 0.05f;

		private const float Helmet = 0.02f;

		private static readonly string[] Pieces = new string[9] { "HelmetFenrir_TW", "ArmorFenrirChest_TW", "ArmorFenrirLegs_TW", "HelmetFenrirBM_TW", "ArmorFenrirBMChest_TW", "ArmorFenrirBMLegs_TW", "HelmetFenrirCarapace_TW", "ArmorFenrirCarapaceChest_TW", "ArmorFenrirCarapaceLegs_TW" };

		internal static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("Fenrir", "Enabled", true, "true = Therzie Fenrir chest and legs give +5 % movement and the helmet +2 % (+12 % for a full set), so the upgrade keeps the Fenris speed it was made from. false = Therzie's own values (+2 % or 0 %).");
		}

		private static void Apply(ObjectDB db, string where)
		{
			if (!Enabled.Value || (Object)(object)db == (Object)null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			string[] pieces = Pieces;
			foreach (string text in pieces)
			{
				GameObject itemPrefab = db.GetItemPrefab(text);
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val == (Object)null)
				{
					num2++;
					continue;
				}
				SharedData shared = val.m_itemData.m_shared;
				float num3 = (text.StartsWith("Helmet") ? 0.02f : 0.05f);
				if (shared.m_movementModifier != num3)
				{
					shared.m_movementModifier = num3;
					num++;
				}
			}
			if (num > 0 || num2 > 0)
			{
				ReefClientPlugin.Log.LogInfo((object)("fenrir (" + where + "): " + num + " piece(s) set (chest/legs +" + 5f + " %, helmet +" + 2f + " % movement), " + num2 + " not found"));
			}
		}
	}
	internal static class ReefFood
	{
		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		internal static class GetTotalFoodValuePatch
		{
			private static bool Prefix(Player __instance, ref float hp, ref float stamina, ref float eitr)
			{
				hp = 0f;
				stamina = 0f;
				eitr = 0f;
				if (Enabled == null || !Enabled.Value)
				{
					return true;
				}
				try
				{
					hp = __instance.m_baseHP;
					stamina = __instance.m_baseStamina;
					float num = hp;
					List<Food> foods = __instance.GetFoods();
					if (foods != null)
					{
						foreach (Food item in foods)
						{
							if (item != null && item.m_item != null && item.m_item.m_shared != null)
							{
								SharedData shared = item.m_item.m_shared;
								float elapsed = Elapsed(item, shared);
								hp += Curve(elapsed, HealthExponent) * shared.m_food;
								stamina += Curve(elapsed, StaminaExponent) * shared.m_foodStamina;
								eitr += Curve(elapsed, EitrExponent) * shared.m_foodEitr;
								num += item.m_health;
							}
						}
						Proof(foods, hp, num);
					}
					return false;
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("food curve: " + ex.GetType().Name + ": " + ex.Message + " — vanilla value used"));
					hp = 0f;
					stamina = 0f;
					eitr = 0f;
					return true;
				}
			}
		}

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> HealthExponent;

		internal static ConfigEntry<float> StaminaExponent;

		internal static ConfigEntry<float> EitrExponent;

		private const float MinExponent = 0.1f;

		private const float MaxExponent = 100f;

		private const string ExponentHelp = "Exponent 'e' in the food curve 'value = (1 - x^e) * base', x = fraction of the buff already elapsed. Higher = full longer then a sharper drop; 1 = linear; below 1 = fast at first, then slower. Range 0.1 - 100.";

		private static int lastFoodCount = -1;

		private static float nextProofAt;

		internal static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("Food", "Enabled", true, "true = The Reef's food curve (absorbed from Sated). false = vanilla decay. Takes effect immediately.");
			HealthExponent = config.Bind<float>("Food", "HealthCurveExponent", 8f, "Added health. Exponent 'e' in the food curve 'value = (1 - x^e) * base', x = fraction of the buff already elapsed. Higher = full longer then a sharper drop; 1 = linear; below 1 = fast at first, then slower. Range 0.1 - 100.");
			StaminaExponent = config.Bind<float>("Food", "StaminaCurveExponent", 8f, "Added stamina. Exponent 'e' in the food curve 'value = (1 - x^e) * base', x = fraction of the buff already elapsed. Higher = full longer then a sharper drop; 1 = linear; below 1 = fast at first, then slower. Range 0.1 - 100.");
			EitrExponent = config.Bind<float>("Food", "EitrCurveExponent", 8f, "Added eitr. Exponent 'e' in the food curve 'value = (1 - x^e) * base', x = fraction of the buff already elapsed. Higher = full longer then a sharper drop; 1 = linear; below 1 = fast at first, then slower. Range 0.1 - 100.");
		}

		private static void Proof(List<Food> foods, float hp, float vanillaHp)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (foods.Count == lastFoodCount && realtimeSinceStartup < nextProofAt)
			{
				return;
			}
			lastFoodCount = foods.Count;
			nextProofAt = realtimeSinceStartup + 300f;
			if (foods.Count == 0)
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (Food food in foods)
			{
				if (food != null && food.m_item != null && food.m_item.m_shared != null)
				{
					SharedData shared = food.m_item.m_shared;
					list.Add(food.m_name + " " + Mathf.RoundToInt(Elapsed(food, shared) * 100f) + "% eaten");
				}
			}
			ReefClientPlugin.Log.LogInfo((object)("food curve: max hp " + Mathf.RoundToInt(hp) + " (vanilla would be " + Mathf.RoundToInt(vanillaHp) + ") — " + string.Join(", ", list.ToArray())));
		}

		private static float Elapsed(Food food, SharedData shared)
		{
			float foodBurnTime = shared.m_foodBurnTime;
			if (foodBurnTime <= 0f)
			{
				return 0f;
			}
			return Mathf.Clamp01(1f - food.m_time / foodBurnTime);
		}

		private static float Curve(float elapsed, ConfigEntry<float> exponent)
		{
			float num = Mathf.Clamp(exponent.Value, 0.1f, 100f);
			return 1f - Mathf.Pow(elapsed, num);
		}
	}
	internal static class ReefForgeGate
	{
		[HarmonyPatch(typeof(Player), "Interact", new Type[]
		{
			typeof(GameObject),
			typeof(bool),
			typeof(bool)
		})]
		internal static class InteractPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Player __instance, GameObject go, bool hold, bool alt)
			{
				try
				{
					if ((Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer)
					{
						return true;
					}
					if (!IsSealed(go))
					{
						return true;
					}
					if (Time.time - lastLineAt >= (LineEverySeconds?.Value ?? 3f))
					{
						lastLineAt = Time.time;
						string text = Line?.Value ?? "";
						if (text.Length > 0)
						{
							((Character)__instance).Message((MessageType)2, text, 0, (Sprite)null, false);
						}
						ManualLogSource log = ReefClientPlugin.Log;
						ZNetView componentInParent = go.GetComponentInParent<ZNetView>();
						log.LogInfo((object)("forge gate: refused " + Utils.GetPrefabName(((componentInParent != null) ? ((Component)componentInParent).gameObject : null) ?? ((Component)go.transform.root).gameObject)));
					}
					return false;
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("forge gate: " + ex.Message + " (letting the interaction through)"));
					return true;
				}
			}
		}

		internal static ConfigEntry<bool> Sealed;

		internal static ConfigEntry<string> Prefabs;

		internal static ConfigEntry<string> Line;

		internal static ConfigEntry<float> LineEverySeconds;

		private static HashSet<string> sealedPrefabs;

		private static float lastLineAt = -999f;

		internal static void Bind(ConfigFile config)
		{
			Sealed = config.Bind<bool>("ForgeOfPotential", "Sealed", true, "true = the Forge of Potential (and every prefab listed below) refuses interaction: no menu, no upgrade, no idol spent. The realm sealed it on 2026-09-16 while a 1.0 bug (every recipe unlocked after use) is investigated. false = vanilla behaviour.");
			Prefabs = config.Bind<string>("ForgeOfPotential", "Prefabs", "UpgradeStation", "Comma-separated prefab names refused while Sealed is true (the station's piece prefab is UpgradeStation).");
			Line = config.Bind<string>("ForgeOfPotential", "Line", "Odin has sealed this forge. Not yet.", "What the player reads when refused (empty = silent).");
			LineEverySeconds = config.Bind<float>("ForgeOfPotential", "LineEverySeconds", 3f, "The line is shown at most once per this many seconds (a held key would spam it).");
			sealedPrefabs = null;
			Prefabs.SettingChanged += delegate
			{
				sealedPrefabs = null;
			};
		}

		private static HashSet<string> Wanted()
		{
			if (sealedPrefabs != null)
			{
				return sealedPrefabs;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			string[] array = (Prefabs?.Value ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			sealedPrefabs = hashSet;
			return hashSet;
		}

		internal static bool IsSealed(GameObject go)
		{
			if ((Object)(object)go == (Object)null || Sealed == null || !Sealed.Value)
			{
				return false;
			}
			ZNetView componentInParent = go.GetComponentInParent<ZNetView>();
			string prefabName = Utils.GetPrefabName(((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).gameObject : ((Component)go.transform.root).gameObject);
			return Wanted().Contains(prefabName);
		}
	}
	internal static class ReefHarvest
	{
		[HarmonyPatch(typeof(Player), "Interact")]
		internal static class InteractPatch
		{
			private static void Prefix(Player __instance, GameObject go, bool hold, bool alt)
			{
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (BulkHarvest == null || !BulkHarvest.Value || (Object)(object)go == (Object)null || (Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer || hold || ((Character)__instance).InAttack() || ((Character)__instance).InDodge() || !ZInput.GetKey(ModifierKey.Value, false))
					{
						return;
					}
					Transform root = go.transform.root;
					if (!((Object)(object)root == (Object)null))
					{
						Pickable componentInParent = go.GetComponentInParent<Pickable>();
						Beehive val = (((Object)(object)componentInParent != (Object)null) ? null : go.GetComponentInParent<Beehive>());
						if (!((Object)(object)componentInParent == (Object)null) || !((Object)(object)val == (Object)null))
						{
							Sweep(__instance, root, hold, alt);
						}
					}
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("harvest: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		internal static ConfigEntry<bool> BulkHarvest;

		internal static ConfigEntry<KeyCode> ModifierKey;

		internal static ConfigEntry<float> Radius;

		internal static ConfigEntry<bool> SameKindOnly;

		internal static ConfigEntry<int> MaxPerHarvest;

		internal static void Bind(ConfigFile config)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			BulkHarvest = config.Bind<bool>("Harvest", "BulkHarvest", true, "true = holding the modifier key while you pick also picks everything else within the radius. Berries, mushrooms, crops, flint, stone, branches and beehives.");
			ModifierKey = config.Bind<KeyCode>("Harvest", "ModifierKey", (KeyCode)304, "Hold this while you pick to harvest in bulk. Same key PlantEasily used, so nothing to relearn.");
			Radius = config.Bind<float>("Harvest", "Radius", 3f, new ConfigDescription("How far around the thing you picked to sweep, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			SameKindOnly = config.Bind<bool>("Harvest", "SameKindOnly", false, "false = take everything harvestable in range. true = take only the same kind as the one you clicked (a carrot patch grown through raspberries comes out as carrots alone).");
			MaxPerHarvest = config.Bind<int>("Harvest", "MaxPerHarvest", 50, new ConfigDescription("Ceiling on one gesture, so a big radius in a berry field cannot hang the game.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
		}

		private static void Sweep(Player player, Transform root, bool hold, bool alt)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Interactable> hashSet = new HashSet<Interactable>();
			Collider[] array = Physics.OverlapSphere(root.position, Radius.Value);
			foreach (Collider val in array)
			{
				Pickable componentInParent = ((Component)val).GetComponentInParent<Pickable>();
				Beehive val2 = (((Object)(object)componentInParent != (Object)null) ? null : ((Component)val).GetComponentInParent<Beehive>());
				if ((Object)(object)componentInParent == (Object)null && (Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Transform root2 = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).gameObject : ((Component)val2).gameObject).transform.root;
				if (!((Object)(object)root2 == (Object)(object)root) && (!SameKindOnly.Value || !(((Object)root2).name != ((Object)root).name)) && (!((Object)(object)val2 != (Object)null) || val2.GetHoneyLevel() >= 1))
				{
					Interactable item;
					if (!((Object)(object)componentInParent != (Object)null))
					{
						Interactable val3 = (Interactable)(object)val2;
						item = val3;
					}
					else
					{
						Interactable val3 = (Interactable)(object)componentInParent;
						item = val3;
					}
					hashSet.Add(item);
				}
			}
			int num = 0;
			foreach (Interactable item2 in hashSet)
			{
				if (num >= MaxPerHarvest.Value)
				{
					break;
				}
				item2.Interact((Humanoid)(object)player, hold, alt);
				num++;
			}
			if (num > 0)
			{
				ReefClientPlugin.Log.LogInfo((object)("harvest: " + Utils.GetPrefabName(((Component)root).gameObject) + " + " + num + " more within " + Radius.Value.ToString("0.#") + " m" + (SameKindOnly.Value ? " (same kind only)" : "")));
			}
		}
	}
	internal static class ReefPortals
	{
		[HarmonyPatch(typeof(TeleportWorld), "UpdatePortal")]
		internal static class UpdatePortalPatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(TeleportWorld __instance)
			{
				try
				{
					if (AlignStandingPortals == null || !AlignStandingPortals.Value)
					{
						return;
					}
					if (!looked)
					{
						Look();
					}
					if (apType == null)
					{
						return;
					}
					Component component = ((Component)__instance).GetComponent(apType);
					if ((Object)(object)component == (Object)null)
					{
						return;
					}
					ZNetScene instance = ZNetScene.instance;
					if ((Object)(object)instance == (Object)null)
					{
						return;
					}
					string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject);
					GameObject prefab = instance.GetPrefab(prefabName);
					if ((Object)(object)prefab == (Object)null)
					{
						return;
					}
					Component component2 = prefab.GetComponent(apType);
					if ((Object)(object)component2 == (Object)null || component2 == component)
					{
						return;
					}
					List<string> list = fAllowedItems.GetValue(component2) as List<string>;
					bool flag = (bool)fAllowEverything.GetValue(component2);
					List<string> list2 = fAllowedItems.GetValue(component) as List<string>;
					bool flag2 = (bool)fAllowEverything.GetValue(component);
					if (flag != flag2 || !Same(list, list2))
					{
						fAllowedItems.SetValue(component, (list == null) ? new List<string>() : new List<string>(list));
						fAllowEverything.SetValue(component, flag);
						if (said.Add(prefabName))
						{
							ReefClientPlugin.Log.LogInfo((object)("portals: " + prefabName + " realigned -> " + (flag ? "everything" : string.Join(", ", (list ?? new List<string>()).ToArray())) + " (was " + (flag2 ? "everything" : string.Join(", ", (list2 ?? new List<string>()).ToArray())) + ")"));
						}
					}
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("portals: " + ex.GetType().Name + ": " + ex.Message));
					apType = null;
				}
			}
		}

		internal static ConfigEntry<bool> AlignStandingPortals;

		private static bool looked;

		private static Type apType;

		private static FieldInfo fAllowedItems;

		private static FieldInfo fAllowEverything;

		private static readonly HashSet<string> said = new HashSet<string>();

		internal static void Bind(ConfigFile config)
		{
			AlignStandingPortals = config.Bind<bool>("Portals", "AlignStandingPortals", true, "true = a portal already standing in the world is put back on the allowlist the server currently sends (AdvancedPortals only applies a config change to the prefab, never to portals already placed). false = AdvancedPortals' own behaviour, which is how iron kept crossing while the server said no.");
		}

		private static void Look()
		{
			looked = true;
			apType = AccessTools.TypeByName("AdvancedPortals.AdvancedPortal");
			if (apType == null)
			{
				ReefClientPlugin.Log.LogInfo((object)"portals: AdvancedPortals not installed, nothing to align");
				return;
			}
			fAllowedItems = AccessTools.Field(apType, "AllowedItems");
			fAllowEverything = AccessTools.Field(apType, "AllowEverything");
			if (fAllowedItems == null || fAllowEverything == null)
			{
				apType = null;
				ReefClientPlugin.Log.LogWarning((object)"portals: AdvancedPortal changed shape (AllowedItems/AllowEverything not found) — not aligning");
			}
			else
			{
				ReefClientPlugin.Log.LogInfo((object)"portals: aligning standing portals on the server's allowlist");
			}
		}

		private static bool Same(List<string> a, List<string> b)
		{
			if (a == null || b == null)
			{
				return a == b;
			}
			if (a.Count != b.Count)
			{
				return false;
			}
			for (int i = 0; i < a.Count; i++)
			{
				if (!string.Equals(a[i], b[i], StringComparison.OrdinalIgnoreCase))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class ReefRevive
	{
		[HarmonyPatch(typeof(Player), "OnDeath")]
		internal static class OnDeathPatch
		{
			private static void Prefix(Player __instance)
			{
				//IL_0135: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if ((Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer || Enabled == null || !Enabled.Value || !OnAServer())
					{
						return;
					}
					ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
					if ((Object)(object)component != (Object)null && component.IsValid() && !component.IsOwner())
					{
						ReefClientPlugin.Log.LogError((object)"revive: DYING ON A VIEW WE DO NOT OWN — vanilla will skip CreateTombStone, the inventory transfer and the skill cost (no grave will appear)");
					}
					snapshot = null;
					Skills skills = ((Character)__instance).GetSkills();
					if ((Object)(object)skills == (Object)null)
					{
						return;
					}
					if (!loggedDeathFactor)
					{
						loggedDeathFactor = true;
						ReefClientPlugin.Log.LogInfo((object)("revive: vanilla death costs " + (skills.m_DeathLowerFactor * Game.m_skillReductionRate).ToString("0.###") + " of every skill (prefab factor " + skills.m_DeathLowerFactor.ToString("0.###") + " x SkillReductionRate " + Game.m_skillReductionRate.ToString("0.###") + ")"));
					}
					Dictionary<SkillType, float> dictionary = new Dictionary<SkillType, float>();
					foreach (Skill skill in skills.GetSkillList())
					{
						if (skill != null && skill.m_info != null)
						{
							dictionary[skill.m_info.m_skill] = skill.m_level;
						}
					}
					snapshot = dictionary;
				}
				catch (Exception ex)
				{
					snapshot = null;
					ReefClientPlugin.Log.LogWarning((object)("revive snapshot: " + ex.GetType().Name + ": " + ex.Message));
				}
			}

			private static void Postfix(Player __instance)
			{
				//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)
				try
				{
					if ((Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer || Enabled == null || !Enabled.Value)
					{
						return;
					}
					if (!OnAServer())
					{
						ReefClientPlugin.Log.LogInfo((object)"revive: local host / solo — vanilla death screen");
						return;
					}
					Game instance = Game.instance;
					if ((Object)(object)instance == (Object)null)
					{
						return;
					}
					float num = Window();
					deadline = Time.realtimeSinceStartup + num;
					keyWasDown = true;
					pendingRestore = false;
					pendingReviver = "";
					waiting = true;
					closing = false;
					((MonoBehaviour)instance).CancelInvoke("_RequestRespawn");
					try
					{
						PlayerProfile playerProfile = instance.GetPlayerProfile();
						if (playerProfile != null)
						{
							playerProfile.SavePlayerData(__instance);
						}
						ReefClientPlugin.Log.LogInfo((object)"revive: corpse written to the profile (no live snapshot can outlive the wait)");
					}
					catch (Exception ex)
					{
						ReefClientPlugin.Log.LogWarning((object)("revive: could not stamp the corpse into the profile — " + ex.GetType().Name + ": " + ex.Message));
					}
					ManualLogSource log = ReefClientPlugin.Log;
					string[] obj = new string[5]
					{
						"revive: waiting room open for ",
						num.ToString(),
						" s (grave at ",
						null,
						null
					};
					Vector3 position = ((Component)__instance).transform.position;
					obj[3] = ((Vector3)(ref position)).ToString("0.0");
					obj[4] = ")";
					log.LogInfo((object)string.Concat(obj));
				}
				catch (Exception ex2)
				{
					waiting = false;
					ReefClientPlugin.Log.LogWarning((object)("revive: " + ex2.GetType().Name + ": " + ex2.Message + " — falling back to the vanilla respawn"));
					try
					{
						Game instance2 = Game.instance;
						if (instance2 != null)
						{
							instance2.RequestRespawn(10f, true);
						}
					}
					catch
					{
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Awake")]
		internal static class AwakeRegisterPatch
		{
			private static void Postfix(Player __instance)
			{
				try
				{
					ZNetView val = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponent<ZNetView>() : null);
					if (!((Object)(object)val == (Object)null) && val.IsValid())
					{
						val.Register<Vector3, string>("Reef_Revive", (Action<long, Vector3, string>)delegate(long sender, Vector3 pos, string who)
						{
							//IL_0007: Unknown result type (might be due to invalid IL or missing references)
							OnRevive(__instance, sender, pos, who);
						});
						val.Register<string>("Reef_ReviveStart", (Action<long, string>)delegate(long sender, string who)
						{
							OnReviveStart(__instance, sender, who);
						});
						if (!loggedRegistered)
						{
							loggedRegistered = true;
							ReefClientPlugin.Log.LogInfo((object)"revive: Reef_Revive registered on a Player view");
						}
					}
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("revive register: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(TombStone), "GetHoverText")]
		internal static class HoverTextPatch
		{
			private static void Postfix(TombStone __instance, ref string __result)
			{
				//IL_0059: 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)
				try
				{
					if (!Active() || (Object)(object)__instance == (Object)null)
					{
						return;
					}
					string text = ((HoverLine != null) ? HoverLine.Value : "");
					if (!string.IsNullOrEmpty(text) && RevivableGrave(Player.m_localPlayer, ((Component)__instance).gameObject, out var _, out var dead))
					{
						string text2 = text.Replace("{key}", ((object)Key()/*cast due to .constrained prefix*/).ToString()).Replace("{name}", dead.GetPlayerName());
						if (string.IsNullOrEmpty(__result))
						{
							__result = text2;
						}
						else
						{
							__result = __result + "  ·  " + text2;
						}
					}
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("revive hover: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(Player), "StartGuardianPower")]
		internal static class GuardianPowerPatch
		{
			private static bool Prefix(Player __instance, ref bool __result)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: 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_0025: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (!Active() || (Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer)
					{
						return true;
					}
					KeyCode val = Key();
					ZNetView graveView;
					Player dead;
					bool flag = (int)val != 0 && ZInput.GetKey(val, false) && RevivableGrave(__instance, ((Humanoid)__instance).GetHoverObject(), out graveView, out dead);
					if (!channeling && !flag)
					{
						return true;
					}
					__result = false;
					return false;
				}
				catch (Exception ex)
				{
					ReefClientPlugin.Log.LogWarning((object)("revive power guard: " + ex.GetType().Name + ": " + ex.Message));
					return true;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnSpawned", new Type[] { typeof(bool) })]
		internal static class OnSpawnedPatch
		{
			private static void Postfix(Player __instance)
			{
				//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
				//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
				//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
				//IL_014d: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (!pendingRestore || (Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer)
					{
						return;
					}
					if (pendingSince < 0f || Time.realtimeSinceStartup - pendingSince > 60f)
					{
						pendingRestore = false;
						snapshot = null;
						ReefClientPlugin.Log.LogWarning((object)"revive: stale pending respawn — nothing restored");
						return;
					}
					pendingRestore = false;
					try
					{
						ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
						if ((Object)(object)component != (Object)null && component.IsValid() && !component.IsOwner())
						{
							component.ClaimOwnership();
							ReefClientPlugin.Log.LogWarning((object)"revive: respawned player view was NOT ours — ownership claimed (without this, the next death creates no grave and costs nothing)");
						}
						else
						{
							ReefClientPlugin.Log.LogInfo((object)"revive: respawned player view is ours");
						}
					}
					catch (Exception ex)
					{
						ReefClientPlugin.Log.LogWarning((object)("revive ownership: " + ex.GetType().Name + ": " + ex.Message));
					}
					if (RestoreSkills != null && RestoreSkills.Value && snapshot != null)
					{
						Skills skills = ((Character)__instance).GetSkills();
						int num = 0;
						if ((Object)(object)skills != (Object)null)
						{
							foreach (Skill skill in skills.GetSkillList())
							{
								if (skill != null && skill.m_info != null && snapshot.TryGetValue(skill.m_info.m_skill, out var value) && value > skill.m_level)
								{
									skill.m_level = value;
									num++;
								}
							}
						}
						ReefClientPlugin.Log.LogInfo((object)("revive: " + num + " skill(s) given back"));
					}
					snapshot = null;
					string text = ((BuffName != null) ? (BuffName.Value ?? "").Trim() : "");
					if (text.Length > 0)
					{
						SEMan sEMan = ((Character)__instance).GetSEMan();
						int stableHashCode = StringExtensionMethods.GetStableHashCode(text);
						if (sEMan == null || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ObjectDB.instance.GetStatusEffect(stableHashCode) == (Object)null)
						{
							ReefClientPlugin.Log.LogWarning((object)("revive: no status effect named '" + text + "' — no buff given"));
						}
						else
						{
							sEMan.AddStatusEffect(stableHashCode, true, 0, 0f, (short)(-1));
							ReefClientPlugin.Log.LogInfo((object)("revive: buff '" + text + "' granted"));
						}
					}
					string text2 = ((RevivedLine != null) ? RevivedLine.Value : "");
					if (!string.IsNullOrEmpty(text2))
					{
						((Character)__instance).Message((MessageType)2, text2.Replace("{name}", string.IsNullOrEmpty(pendingReviver) ? "?" : pendingReviver), 0, (Sprite)null, false);
					}
					Vector3 position = ((Component)__instance).transform.position;
					float num2 = Vector3.Distance(position, pendingPos);
					if (num2 > 8f)
					{
						ReefClientPlugin.Log.LogWarning((object)("revive: logout point rejected — spawned at " + ((Vector3)(ref position)).ToString("0.0") + ", " + num2.ToString("0") + " m from the grave at " + ((Vector3)(ref pendingPos)).ToString("0.0") + " (vanilla bed spawn)"));
					}
					else
					{
						ReefClientPlugin.Log.LogInfo((object)("revive: back on the grave at " + ((Vector3)(ref position)).ToString("0.0") + " (" + num2.ToString("0.0") + " m from it)"));
					}
					pendingReviver = "";
				}
				catch (Exception ex2)
				{
					pendingRestore = false;
					snapshot = null;
					ReefClientPlugin.Log.LogWarning((object)("revive spawn: " + ex2.GetType().Name + ": " + ex2.Message));
				}
			}
		}

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> WindowSeconds;

		internal static ConfigEntry<KeyCode> RespawnKey;

		internal static ConfigEntry<bool> RestoreSkills;

		internal static ConfigEntry<string> BuffName;

		internal static ConfigEntry<string> TitleLine;

		internal static ConfigEntry<string> WaitLine;

		internal static ConfigEntry<string> RespawnLine;

		internal static ConfigEntry<string> RevivedLine;

		internal static ConfigEntry<string> ReviverLine;

		internal static ConfigEntry<float> HudSize;

		internal static ConfigEntry<KeyCode> ReviveKey;

		internal static ConfigEntry<float> ChannelSeconds;

		internal static ConfigEntry<string> HoverLine;

		internal static ConfigEntry<string> ChannelLine;

		internal static ConfigEntry<string> BeingRaisedLine;

		internal static ConfigEntry<string> CancelLine;

		internal const string RpcName = "Reef_Revive";

		internal const string StartRpcName = "Reef_ReviveStart";

		private const string OldWaitLine = "A friend can raise you by touching your grave.";

		private static bool waiting;

		private static bool closing;

		private static float deadline;

		private static bool keyWasDown;

		private static bool loggedDeathFactor;

		private static bool loggedRegistered;

		private static bool pendingRestore;

		private static float pendingSince = -1f;

		private static string pendingReviver = "";

		private static Vector3 pendingPos;

		private static Dictionary<SkillType, float> snapshot;

		private static readonly Dictionary<long, float> lastSendAt = new Dictionary<long, float>();

		private static bool channeling;

		private static float channelStart;

		private static Vector3 channelFrom;

		private static ZDOID channelGrave;

		private static ZNetView channelGraveView;

		private static Player channelTarget;

		private static float channelLastAimed;

		private const float AimGrace = 0.5f;

		private static long channelOwner;

		private static string channelName = "";

		private static bool channelSat;

		private static bool reviveKeyWasDown;

		private static GameObject bar;

		private static RectTransform barFill;

		private static TMP_Text barLabel;

		private static bool barFailed;

		private const float BarWidth = 180f;

		private static string raisedBy = "";

		private static float raisedShownUntil = -1f;

		private static Func<Player, bool> takeInput;

		private static Action<Player> stopEmote;

		private static GameObject panel;

		private static TMP_Text label;

		private static float builtSize = -1f;

		private static bool buildFailed;

		internal static void Bind(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("Revive", "Enabled", true, "true = the death screen becomes a waiting room: a friend who holds the revive key on your grave sends you back on top of it. false = the grave, its hover text, the death screen and the F key (Forsaken power) are all 100 % vanilla. Disabled on a solo / local-host world, where vanilla owns the death screen.");
			WindowSeconds = cfg.Bind<float>("Revive", "WindowSeconds", 60f, "How long you can wait for a friend before the vanilla respawn happens anyway. Clamped to 10-180 s.");
			RespawnKey = cfg.Bind<KeyCode>("Revive", "RespawnKey", (KeyCode)32, "Pressed on the death screen = respawn now, exactly as vanilla would have after its own 10 s.");
			RestoreSkills = cfg.Bind<bool>("Revive", "RestoreSkills", true, "true = being raised by a friend also gives you back the skill levels the death took (measured at 5 % of every skill on this realm). Dying alone and respawning keeps the full vanilla cost either way — only a revive restores. false = a revive saves you the walk and nothing else.");
			BuffName = cfg.Bind<string>("Revive", "BuffName", "Rested", "Status effect granted when you are revived (a vanilla effect name; empty = no buff). Looked up in ObjectDB; a name that does not exist is logged as a warning and simply skipped.");
			TitleLine = cfg.Bind<string>("Revive", "TitleLine", "You have fallen.", "First line of the waiting room (empty = hidden).");
			WaitLine = cfg.Bind<string>("Revive", "WaitLine", "A friend can raise you: they hold F on your grave.", "Second line of the waiting room (empty = hidden).");
			if (WaitLine.Value == "A friend can raise you by touching your grave.")
			{
				WaitLine.Value = (string)((ConfigEntryBase)WaitLine).DefaultValue;
				ReefClientPlugin.Log.LogInfo((object)"revive: WaitLine still held the 0.6.1 default — updated to the 0.6.2 one");
			}
			RespawnLine = cfg.Bind<string>("Revive", "RespawnLine", "[{key}] Respawn now", "Last line of the waiting room. {key} is replaced by RespawnKey (empty = hidden).");
			RevivedLine = cfg.Bind<string>("Revive", "RevivedLine", "Raised by {name}. Your grave is beside you.", "Centre message when you come back on your grave. {name} = who raised you (empty = silent). The belongings stay IN the grave — being raised puts you next to it, it does not hand the bag back.");
			ReviverLine = cfg.Bind<string>("Revive", "ReviverLine", "You raise {name}.", "Centre message for the one who touches the grave. {name} = the dead player (empty = silent).");
			HudSize = cfg.Bind<float>("Revive", "HudSize", 28f, "Text height in px on the death screen.");
			ReviveKey = cfg.Bind<KeyCode>("Revive", "ReviveKey", (KeyCode)102, "Hold it on a dead friend's grave to raise them. F is also vanilla's Forsaken power key: while you aim at a grave you can revive with this key down, the power does not fire (anywhere else it does).");
			ChannelSeconds = cfg.Bind<float>("Revive", "ChannelSeconds", 5f, "How long the revive key must be held. Clamped to 1-15 s. Letting go, moving or looking away cancels.");
			HoverLine = cfg.Bind<string>("Revive", "HoverLine", "[<color=yellow><b>Hold {key}</b></color>] Revive {name}", "Added to the grave's hover text when its owner can be raised. {key} = ReviveKey, {name} = the dead player (empty = no hint, the revive still works).");
			ChannelLine = cfg.Bind<string>("Revive", "ChannelLine", "Raising {name}…", "Above the bar while you hold the key. {name} = the dead player.");
			BeingRaisedLine = cfg.Bind<string>("Revive", "BeingRaisedLine", "{name} is raising you…", "On YOUR death screen when a friend starts to raise you (your countdown starts over at the same moment). {name} = the friend (empty = hidden).");
			CancelLine = cfg.Bind<string>("Revive", "CancelLine", "Revive interrupted.", "Message when you let go, move or look away before the end (empty = silent).");
			takeInput = TryDelegate<Func<Player, bool>>("TakeInput");
			stopEmote = TryDelegate<Action<Player>>("StopEmote");
		}

		private static T TryDelegate<T>(string method) where T : Delegate
		{
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Player), method, Type.EmptyTypes, (Type[])null);
				if (methodInfo != null)
				{
					return AccessTools.MethodDelegate<T>(methodInfo, (object)null, true);
				}
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("revive: Player." + method + " not reachable — " + ex.GetType().Name + ": " + ex.Message));
				return null;
			}
			ReefClientPlugin.Log.LogWarning((object)("revive: Player." + method + " not found"));
			return null;
		}

		private static float Channel()
		{
			return Mathf.Clamp((ChannelSeconds != null) ? ChannelSeconds.Value : 5f, 1f, 15f);
		}

		private static KeyCode Key()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (ReviveKey == null)
			{
				return (KeyCode)102;
			}
			return ReviveKey.Value;
		}

		private static float Window()
		{
			return Mathf.Clamp((WindowSeconds != null) ? WindowSeconds.Value : 60f, 10f, 180f);
		}

		private static bool OnAServer()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance != (Object)null && !instance.IsServer() && !instance.IsDedicated())
			{
				return ZRoutedRpc.instance != null;
			}
			return false;
		}

		private static void OnReviveStart(Player me, long sender, string who)
		{
			try
			{
				ReefClientPlugin.Log.LogInfo((object)("revive: Reef_ReviveStart received from " + (string.IsNullOrEmpty(who) ? "?" : who) + " (peer " + sender + ")"));
				if (!waiting || closing)
				{
					ReefClientPlugin.Log.LogInfo((object)"revive: not waiting — channel start ignored");
				}
				else if (!((Object)(object)me == (Object)null) && me == Player.m_localPlayer)
				{
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					deadline = Mathf.Max(deadline, realtimeSinceStartup + Window());
					raisedBy = who ?? "";
					raisedShownUntil = realtimeSinceStartup + Channel() + 2f;
					ReefClientPlugin.Log.LogInfo((object)("revive: window restarted at " + Window() + " s — " + raisedBy + " is channelling"));
				}
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("revive start receive: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static void OnRevive(Player me, long sender, Vector3 pos, string who)
		{
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: 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)
			try
			{
				ReefClientPlugin.Log.LogInfo((object)("revive: Reef_Revive received from " + (string.IsNullOrEmpty(who) ? "?" : who) + " (peer " + sender + ") at " + ((Vector3)(ref pos)).ToString("0.0")));
				if (!waiting)
				{
					ReefClientPlugin.Log.LogInfo((object)"revive: not waiting — ignored");
					return;
				}
				if ((Object)(object)me == (Object)null || me != Player.m_localPlayer)
				{
					ReefClientPlugin.Log.LogInfo((object)"revive: not the local player — ignored");
					return;
				}
				Game instance = Game.instance;
				PlayerProfile val = (((Object)(object)instance != (Object)null) ? instance.GetPlayerProfile() : null);
				if ((Object)(object)instance == (Object)null || val == null)
				{
					ReefClientPlugin.Log.LogWarning((object)"revive: no profile — ignored");
					return;
				}
				if (closing)
				{
					ReefClientPlugin.Log.LogInfo((object)"revive: a respawn is already under way — revive ignored");
					return;
				}
				closing = true;
				CloseWaitingRoom();
				pendingRestore = true;
				pendingSince = Time.realtimeSinceStartup;
				pendingReviver = who ?? "";
				pendingPos = pos;
				val.SetLogoutPoint(pos);
				instance.RequestRespawn(0f, false);
				ReefClientPlugin.Log.LogInfo((object)("revive: respawning on the grave at " + ((Vector3)(ref pos)).ToString("0.0")));
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("revive receive: " + ex.GetType().Name + ": " + ex.Message));
				try
				{
					CloseWaitingRoom();
					Game instance2 = Game.instance;
					if (instance2 != null)
					{
						instance2.RequestRespawn(0f, true);
					}
				}
				catch
				{
				}
			}
		}

		private static bool RevivableGrave(Player me, GameObject go, out ZNetView graveView, out Player dead)
		{
			graveView = null;
			dead = null;
			if ((Object)(object)me == (Object)null || (Object)(object)go == (Object)null)
			{
				return false;
			}
			ZNetView componentInParent = go.GetComponentInParent<ZNetView>();
			if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsValid())
			{
				return false;
			}
			if ((Object)(object)((Component)componentInParent).gameObject.GetComponent<TombStone>() == (Object)null)
			{
				return false;
			}
			long num = componentInParent.GetZDO().GetLong(ZDOVars.s_owner, 0L);
			if (num == 0L || num == me.GetPlayerID())
			{
				return false;
			}
			Player player = Player.GetPlayer(num);
			if ((Object)(object)player == (Object)null || !((Character)player).IsDead())
			{
				return false;
			}
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return false;
			}
			graveView = componentInParent;
			dead = player;
			return true;
		}

		private static bool Active()
		{
			if (Enabled != null && Enabled.Value)
			{
				return OnAServer();
			}
			return false;
		}

		private static void ChannelTick()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: 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_0098: 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_031f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			KeyCode val = Key();
			bool flag = (int)val != 0 && ZInput.GetKey(val, false);
			bool flag2 = flag && !reviveKeyWasDown;
			reviveKeyWasDown = flag;
			if (!channeling)
			{
				if (flag2 && Active() && !((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && (takeInput == null || takeInput(localPlayer)) && RevivableGrave(localPlayer, ((Humanoid)localPlayer).GetHoverObject(), out var graveView, out var dead))
				{
					channeling = true;
					channelStart = Time.realtimeSinceStartup;
					channelFrom = ((Component)localPlayer).transform.position;
					channelGrave = graveView.GetZDO().m_uid;
					channelGraveView = graveView;
					channelTarget = dead;
					channelLastAimed = channelStart;
					channelOwner = graveView.GetZDO().GetLong(ZDOVars.s_owner, 0L);
					channelName = dead.GetPlayerName();
					channelSat = localPlayer.StartEmote("sit", false);
					((Component)dead).GetComponent<ZNetView>().InvokeRPC("Reef_ReviveStart", new object[1] { localPlayer.GetPlayerName() });
					ReefClientPlugin.Log.LogInfo((object)("revive: channel started on the grave of " + channelName + " (" + Channel() + " s, sat: " + channelSat + "), Reef_ReviveStart sent"));
				}
				return;
			}
			string text = null;
			ZNetView val2 = channelGraveView;
			Player val3 = channelTarget;
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (RevivableGrave(localPlayer, ((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetHoverObject() : null, out var graveView2, out var _) && graveView2.GetZDO().m_uid == channelGrave)
			{
				channelLastAimed = realtimeSinceStartup;
			}
			if (!Active() || (Object)(object)localPlayer == (Object)null)
			{
				text = "disabled";
			}
			else if (((Character)localPlayer).IsDead())
			{
				text = "reviver died";
			}
			else if (!flag)
			{
				text = "key released";
			}
			else if (takeInput != null && !takeInput(localPlayer))
			{
				text = "a window opened";
			}
			else if ((Object)(object)val2 == (Object)null || !val2.IsValid() || (Object)(object)val3 == (Object)null || !((Character)val3).IsDead())
			{
				text = "the dead player is gone or already up";
			}
			else if (realtimeSinceStartup - channelLastAimed > 0.5f)
			{
				text = "grave no longer aimed at";
			}
			else
			{
				Vector3 val4 = ((Component)localPlayer).transform.position - channelFrom;
				val4.y = 0f;
				if (((Vector3)(ref val4)).magnitude > 0.5f)
				{
					text = "moved";
				}
			}
			if (text != null)
			{
				EndChannel(localPlayer, "cancelled (" + text + ")", cancelled: true);
				return;
			}
			float num = (realtimeSinceStartup - channelStart) / Channel();
			if (num < 1f)
			{
				DrawBar(num);
				return;
			}
			if (!lastSendAt.TryGetValue(channelOwner, out var value) || !(realtimeSinceStartup - value < 2f))
			{
				lastSendAt[channelOwner] = realtimeSinceStartup;
				Vector3 position = ((Component)val2).transform.position;
				ZNetView component = ((Component)val3).GetComponent<ZNetView>();
				component.InvokeRPC("Reef_Revive", new object[2]
				{
					position,
					localPlayer.GetPlayerName()
				});
				ReefClientPlugin.Log.LogInfo((object)("revive: Reef_Revive sent to " + channelName + " (playerID " + channelOwner + ", zdo owner " + component.GetZDO().GetOwner() + ") at " + ((Vector3)(ref position)).ToString("0.0")));
				string text2 = ((ReviverLine != null) ? ReviverLine.Value : "");
				if (!string.IsNullOrEmpty(text2))
				{
					((Character)localPlayer).Message((MessageType)2, text2.Replace("{name}", channelName), 0, (Sprite)null, false);
				}
			}
			EndChannel(localPlayer, "complete", cancelled: false);
		}

		private static void EndChannel(Player me, string how, bool cancelled)
		{
			channeling = false;
			HideBar();
			try
			{
				if (channelSat && (Object)(object)me != (Object)null && !((Character)me).IsDead() && stopEmote != null)
				{
					stopEmote(me);
				}
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("revive stand up: " + ex.GetType().Name + ": " + ex.Message));
			}
			channelSat = false;
			channelGraveView = null;
			channelTarget = null;
			ReefClientPlugin.Log.LogInfo((object)("revive: channel on the grave of " + channelName + " " + how));
			if (cancelled && (Object)(object)me != (Object)null && !((Character)me).IsDead())
			{
				string text = ((CancelLine != null) ? CancelLine.Value : "");
				if (!string.IsNullOrEmpty(text))
				{
					((Character)me).Message((MessageType)2, text, 0, (Sprite)null, false);
				}
			}
		}

		private static void DrawBar(float progress)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (barFailed)
			{
				return;
			}
			try
			{
				if ((Object)(object)bar == (Object)null)
				{
					BuildBar();
				}
				if (!((Object)(object)bar == (Object)null))
				{
					if (!bar.activeSelf)
					{
						bar.SetActive(true);
					}
					barFill.sizeDelta = new Vector2(180f * Mathf.Clamp01(progress), 0f);
					string text = ((ChannelLine != null) ? ChannelLine.Value : "");
					barLabel.text = (string.IsNullOrEmpty(text) ? "" : text.Replace("{name}", channelName));
				}
			}
			catch (Exception ex)
			{
				barFailed = true;
				ReefClientPlugin.Log.LogWarning((object)("revive bar: " + ex.GetType().Name + ": " + ex.Message + " — the channel runs without a bar"));
			}
		}

		private static void HideBar()
		{
			if ((Object)(object)bar != (Object)null)
			{
				bar.SetActive(false);
			}
		}

		private static void BuildBar()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Unknown result type (might be due to invalid IL or missing references)
			Hud instance = Hud.instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_crosshair == (Object)null))
			{
				bar = new GameObject("ReefReviveBar", new Type[1] { typeof(RectTransform) });
				RectTransform component = bar.GetComponent<RectTransform>();
				((Transform)component).SetParent(((Component)instance.m_crosshair).transform, false);
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(0.5f, 0.5f);
				component.anchorMax = val;
				component.anchorMin = val;
				component.pivot = new Vector2(0.5f, 0.5f);
				component.anchoredPosition = new Vector2(0f, -48f);
				component.sizeDelta = new Vector2(180f, 8f);
				GameObject val2 = new GameObject("Back", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(Image)
				});
				RectTransform component2 = val2.GetComponent<RectTransform>();
				((Transform)component2).SetParent((Transform)(object)component, false);
				component2.anchorMin = Vector2.zero;
				component2.anchorMax = Vector2.one;
				component2.offsetMin = Vector2.zero;
				component2.offsetMax = Vector2.zero;
				Image component3 = val2.GetComponent<Image>();
				((Graphic)component3).color = new Color(0f, 0f, 0f, 0.6f);
				((Graphic)component3).raycastTarget = false;
				GameObject val3 = new GameObject("Fill", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(Image)
				});
				barFill = val3.GetComponent<RectTransform>();
				((Transform)barFill).SetParent((Transform)(object)component, false);
				barFill.anchorMin = new Vector2(0f, 0f);
				barFill.anchorMax = new Vector2(0f, 1f);
				barFill.pivot = new Vector2(0f, 0.5f);
				barFill.anchoredPosition = Vector2.zero;
				barFill.sizeDelta = Vector2.zero;
				Image component4 = val3.GetComponent<Image>();
				((Graphic)component4).color = new Color(0.92f, 0.8f, 0.45f, 0.95f);
				((Graphic)component4).raycastTarget = false;
				GameObject val4 = new GameObject("Text", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(TextMeshProUGUI)
				});
				RectTransform component5 = val4.GetComponent<RectTransform>();
				((Transform)component5).SetParent((Transform)(object)component, false);
				((Vector2)(ref val))..ctor(0.5f, 1f);
				component5.anchorMax = val;
				component5.anchorMin = val;
				component5.pivot = new Vector2(0.5f, 0f);
				component5.anchoredPosition = new Vector2(0f, 4f);
				component5.sizeDelta = new Vector2(360f, 24f);
				barLabel = (TMP_Text)(object)val4.GetComponent<TextMeshProUGUI>();
				TextMeshProUGUI hoverName = instance.m_hoverName;
				if ((Object)(object)hoverName != (Object)null)
				{
					barLabel.font = ((TMP_Text)hoverName).font;
					barLabel.fontSharedMaterial = ((TMP_Text)hoverName).fontSharedMaterial;
				}
				((Graphic)barLabel).color = new Color(0.92f, 0.88f, 0.78f, 1f);
				barLabel.fontSize = 18f;
				barLabel.alignment = (TextAlignmentOptions)1026;
				((Graphic)barLabel).raycastTarget = false;
				barLabel.text = "";
				ReefClientPlugin.Log.LogInfo((object)"revive: channel bar built under the reticle");
			}
		}

		internal unsafe static void Tick()
		{
			//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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ChannelTick();
			}
			catch (Exception ex)
			{
				ReefClientPlugin.Log.LogWarning((object)("revive channel: " + ex.GetType().Name + ": " + ex.Message));
				if (channeling)
				{
					try
					{
						EndChannel(Player.m_localPlayer, "dropped (error)", cancelled: false);
					}
					catch
					{
						channeling = false;
					}
				}
			}
			if (!waiting)
			{
				return;
			}
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).IsDead())
				{
					CloseWaitingRoom();
					return;
				}
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				float num = deadline - realtimeSinceStartup;
				if (num < -30f)
				{
					ReefClientPlugin.Log.LogError((object)"revive: waiting room overran by 30 s — forcing the vanilla respawn");
					RespawnNow("failsafe");
					return;
				}
				KeyCode val = (KeyCode)((RespawnKey == null) ? 32 : ((int)RespawnKey.Value));
				bool flag = (int)val !=