Decompiled source of SpongePEAKLib v1.0.0

plugins/com.sponge.peaklib.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MonoMod.Utils;
using Photon.Pun;
using Photon.Realtime;
using SpongePEAK.Core.Config;
using SpongePEAK.Core.Game;
using SpongePEAK.Core.Logging;
using SpongePEAK.Core.Patching;
using SpongePEAK.Lib.Items;
using SpongePEAK.Lib.Mods;
using SpongePEAK.Lib.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Localization;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Core;
using Zorro.Settings;
using Zorro.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("sponge")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) sponge")]
[assembly: AssemblyDescription("Modding API tier: plugin base, game event bus and networking helpers.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ddcbf023fcb4c2065ad9b038910633cd4a3a93b0")]
[assembly: AssemblyProduct("SpongePEAKLib")]
[assembly: AssemblyTitle("com.sponge.peaklib")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SpongePEAK.Lib.UI
{
	public sealed class HudBar
	{
		private readonly RectTransform _root;

		private readonly RectTransform _fill;

		private readonly Image _fillImage;

		private float _value = 1f;

		public bool IsValid
		{
			get
			{
				if ((Object)(object)_root != (Object)null)
				{
					return (Object)(object)_fill != (Object)null;
				}
				return false;
			}
		}

		public RectTransform Transform => _root;

		public float Value
		{
			get
			{
				return _value;
			}
			set
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				_value = Mathf.Clamp01(value);
				if ((Object)(object)_fill != (Object)null)
				{
					_fill.anchorMax = new Vector2(_value, 1f);
				}
			}
		}

		public Color Color
		{
			get
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)_fillImage != (Object)null))
				{
					return Color.white;
				}
				return ((Graphic)_fillImage).color;
			}
			set
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_fillImage != (Object)null)
				{
					((Graphic)_fillImage).color = value;
				}
			}
		}

		public static HudBar Create(string name, float width = 120f, float height = 10f)
		{
			Transform root = HudRoot.Root;
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			return new HudBar(name, root, width, height);
		}

		private HudBar(string name, Transform parent, float width, float height)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00d8: 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_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			_root = (RectTransform)val.transform;
			((Transform)_root).SetParent(parent, false);
			_root.sizeDelta = new Vector2(width, height);
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).color = new Color(0f, 0f, 0f, 0.55f);
			((Graphic)obj).raycastTarget = false;
			GameObject val2 = new GameObject("Fill", new Type[1] { typeof(RectTransform) });
			_fill = (RectTransform)val2.transform;
			((Transform)_fill).SetParent((Transform)(object)_root, false);
			_fill.anchorMin = new Vector2(0f, 0f);
			_fill.anchorMax = new Vector2(1f, 1f);
			_fill.offsetMin = new Vector2(1f, 1f);
			_fill.offsetMax = new Vector2(-1f, -1f);
			_fillImage = val2.AddComponent<Image>();
			((Graphic)_fillImage).color = Color.white;
			((Graphic)_fillImage).raycastTarget = false;
		}

		public void SetVisible(bool visible)
		{
			if ((Object)(object)_root != (Object)null && ((Component)_root).gameObject.activeSelf != visible)
			{
				((Component)_root).gameObject.SetActive(visible);
			}
		}

		public void Destroy()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_root).gameObject);
			}
		}
	}
	public static class HudRoot
	{
		private static Transform _root;

		private static ModLog _log;

		private const string RootName = "SpongePEAK_HUD";

		public static bool IsReady => (Object)(object)_root != (Object)null;

		public static Transform Root
		{
			get
			{
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				//IL_006c: 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_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_0081: Unknown result type (might be due to invalid IL or missing references)
				//IL_0082: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				//IL_008d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Expected O, but got Unknown
				if ((Object)(object)_root != (Object)null)
				{
					return _root;
				}
				GUIManager instance = GUIManager.instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance.hudCanvas == (Object)null)
				{
					return null;
				}
				RectTransform val = (RectTransform)new GameObject("SpongePEAK_HUD", new Type[1] { typeof(RectTransform) }).transform;
				((Transform)val).SetParent(((Component)instance.hudCanvas).transform, false);
				val.anchorMin = Vector2.zero;
				val.anchorMax = Vector2.one;
				val.offsetMin = Vector2.zero;
				val.offsetMax = Vector2.zero;
				((Component)val).gameObject.AddComponent<CanvasGroup>().blocksRaycasts = false;
				_root = (Transform)val;
				ModLog log = _log;
				if (log != null)
				{
					log.Debug((object)"HUD root attached to the game's hudCanvas.");
				}
				return _root;
			}
		}

		public static void SetLogger(ModLog log)
		{
			_log = log;
		}

		public static TextMeshProUGUI CreateLabel(string name, UiAnchor anchor = UiAnchor.TopLeft, float fontSize = 18f)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			Transform root = Root;
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(root, false);
			TextMeshProUGUI val3 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val3).fontSize = fontSize;
			((Graphic)val3).color = Color.white;
			((Graphic)val3).raycastTarget = false;
			((TMP_Text)val3).outlineWidth = 0.2f;
			((TMP_Text)val3).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue);
			ApplyAnchor(val2, val3, anchor);
			return val3;
		}

		private static void ApplyAnchor(RectTransform rect, TextMeshProUGUI text, UiAnchor anchor)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: 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_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			rect.sizeDelta = new Vector2(600f, 40f);
			switch (anchor)
			{
			case UiAnchor.TopLeft:
				SetAnchor(rect, new Vector2(0f, 1f));
				rect.anchoredPosition = new Vector2(20f, -20f);
				((TMP_Text)text).alignment = (TextAlignmentOptions)257;
				break;
			case UiAnchor.TopRight:
				SetAnchor(rect, new Vector2(1f, 1f));
				rect.anchoredPosition = new Vector2(-20f, -20f);
				((TMP_Text)text).alignment = (TextAlignmentOptions)260;
				break;
			case UiAnchor.BottomLeft:
				SetAnchor(rect, new Vector2(0f, 0f));
				rect.anchoredPosition = new Vector2(20f, 20f);
				((TMP_Text)text).alignment = (TextAlignmentOptions)1025;
				break;
			case UiAnchor.BottomRight:
				SetAnchor(rect, new Vector2(1f, 0f));
				rect.anchoredPosition = new Vector2(-20f, 20f);
				((TMP_Text)text).alignment = (TextAlignmentOptions)1028;
				break;
			case UiAnchor.Center:
				SetAnchor(rect, new Vector2(0.5f, 0.5f));
				rect.anchoredPosition = Vector2.zero;
				((TMP_Text)text).alignment = (TextAlignmentOptions)514;
				break;
			case UiAnchor.World:
				SetAnchor(rect, new Vector2(0.5f, 0.5f));
				((TMP_Text)text).alignment = (TextAlignmentOptions)514;
				break;
			}
		}

		private static void SetAnchor(RectTransform rect, Vector2 anchor)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = anchor;
			rect.anchorMax = anchor;
			rect.pivot = anchor;
		}

		public static bool PositionAtWorldPoint(RectTransform rect, Vector3 worldPoint, Camera camera = null)
		{
			//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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rect == (Object)null)
			{
				return false;
			}
			if (camera == null)
			{
				camera = Camera.main;
			}
			if ((Object)(object)camera == (Object)null)
			{
				return false;
			}
			Vector3 val = camera.WorldToViewportPoint(worldPoint);
			if (val.z <= 0f)
			{
				return false;
			}
			Transform parent = ((Transform)rect).parent;
			RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			Rect rect2 = val2.rect;
			Vector2 size = ((Rect)(ref rect2)).size;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			rect.pivot = val3;
			Vector2 anchorMin = (rect.anchorMax = val3);
			rect.anchorMin = anchorMin;
			rect.anchoredPosition = new Vector2((val.x - 0.5f) * size.x, (val.y - 0.5f) * size.y);
			return true;
		}

		public static void Reset()
		{
			_root = null;
		}
	}
	public enum UiAnchor
	{
		TopLeft,
		TopRight,
		BottomLeft,
		BottomRight,
		Center,
		World
	}
	public static class MenuIntegration
	{
		private sealed class MenuButtonRequest
		{
			public string Label;

			public Action OnClick;
		}

		private static readonly List<MenuButtonRequest> PauseButtons = new List<MenuButtonRequest>();

		private static readonly List<MenuButtonRequest> MainMenuButtons = new List<MenuButtonRequest>();

		private static ModLog _log;

		private static bool _patched;

		public static void SetLogger(ModLog log)
		{
			_log = log;
			PeakStyle.SetLogger(log);
		}

		public static void AddPauseMenuButton(string label, Action onClick)
		{
			if (!string.IsNullOrEmpty(label) && onClick != null)
			{
				PauseButtons.Add(new MenuButtonRequest
				{
					Label = label,
					OnClick = onClick
				});
				ModLog log = _log;
				if (log != null)
				{
					log.Debug((object)("Registered pause menu button '" + label + "'."));
				}
			}
		}

		public static void AddMainMenuButton(string label, Action onClick)
		{
			if (!string.IsNullOrEmpty(label) && onClick != null)
			{
				MainMenuButtons.Add(new MenuButtonRequest
				{
					Label = label,
					OnClick = onClick
				});
				ModLog log = _log;
				if (log != null)
				{
					log.Debug((object)("Registered main menu button '" + label + "'."));
				}
			}
		}

		public static void Install(Harmony harmony)
		{
			if (_patched || harmony == null)
			{
				return;
			}
			try
			{
				harmony.PatchAll(typeof(MenuIntegrationPatches));
				_patched = true;
				ModLog log = _log;
				if (log != null)
				{
					log.Debug((object)"Menu integration hooks installed.");
				}
			}
			catch (Exception ex)
			{
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Exception("Failed to install menu integration hooks", ex);
				}
			}
		}

		internal static void PopulatePauseMenu(PauseMenuMainPage page)
		{
			if ((Object)(object)page == (Object)null || PauseButtons.Count == 0)
			{
				return;
			}
			try
			{
				Button resumeButton = page.resumeButton;
				if ((Object)(object)resumeButton == (Object)null)
				{
					return;
				}
				Transform parent = ((Component)resumeButton).transform.parent;
				if ((Object)(object)parent == (Object)null)
				{
					return;
				}
				foreach (MenuButtonRequest pauseButton in PauseButtons)
				{
					CreateOnce(parent, pauseButton);
				}
			}
			catch (Exception ex)
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Exception("Failed to populate the pause menu", ex);
				}
			}
		}

		internal static void PopulateMainMenu(MainMenuMainPage page)
		{
			if ((Object)(object)page == (Object)null || MainMenuButtons.Count == 0)
			{
				return;
			}
			try
			{
				Button value = Traverse.Create((object)page).Field("m_settingsButton").GetValue<Button>();
				Transform val = (((Object)(object)value != (Object)null) ? ((Component)value).transform.parent : ((Component)page).transform);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				foreach (MenuButtonRequest mainMenuButton in MainMenuButtons)
				{
					CreateOnce(val, mainMenuButton);
				}
			}
			catch (Exception ex)
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Exception("Failed to populate the main menu", ex);
				}
			}
		}

		private static void CreateOnce(Transform parent, MenuButtonRequest request)
		{
			string text = "SpongeUI_Button_" + request.Label;
			for (int i = 0; i < parent.childCount; i++)
			{
				if (((Object)parent.GetChild(i)).name == text)
				{
					return;
				}
			}
			PeakStyle.CreateButton(request.Label, parent, request.OnClick);
		}
	}
	internal static class MenuIntegrationPatches
	{
		[HarmonyPatch(typeof(MainMenu), "Start")]
		[HarmonyPostfix]
		private static void MainMenuStarted()
		{
			PeakStyle.TryCapture();
		}

		[HarmonyPatch(typeof(MainMenuMainPage), "Start")]
		[HarmonyPostfix]
		private static void MainMenuPageStarted(MainMenuMainPage __instance)
		{
			PeakStyle.TryCapture();
			MenuIntegration.PopulateMainMenu(__instance);
		}

		[HarmonyPatch(typeof(PauseMenuMainPage), "OnEnable")]
		[HarmonyPostfix]
		private static void PauseMenuPageEnabled(PauseMenuMainPage __instance)
		{
			MenuIntegration.PopulatePauseMenu(__instance);
		}
	}
	public sealed class ModalCursorWindow : MenuWindow
	{
		private ModLog _log;

		public Action Closed;

		public override bool showCursorWhileOpen => true;

		public override bool blocksPlayerInput => true;

		public override bool openOnStart => false;

		public override bool closeOnUICancel => true;

		public override bool selectOnOpen => false;

		public void SetLogger(ModLog log)
		{
			_log = log;
		}

		public void SetOpen(bool open)
		{
			try
			{
				MethodInfo method = typeof(MenuWindow).GetMethod(open ? "Open" : "Close", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					ModLog log = _log;
					if (log != null)
					{
						log.Warning((object)("MenuWindow." + (open ? "Open" : "Close") + " not found; the game may have changed."));
					}
					return;
				}
				method.Invoke(this, null);
				GUIManager instance = GUIManager.instance;
				if (instance != null)
				{
					instance.UpdateWindowStatus();
				}
			}
			catch (Exception ex)
			{
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Exception("Could not " + (open ? "open" : "close") + " the window", ex);
				}
			}
		}

		protected override void OnClose()
		{
			((MenuWindow)this).OnClose();
			try
			{
				Closed?.Invoke();
			}
			catch (Exception ex)
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Exception("A window close handler threw", ex);
				}
			}
		}
	}
	public static class PeakStyle
	{
		private const string ButtonSourceName = "UI_MainMenuButton_LeaveGame (2)";

		private const string SettingsCellName = "SettingsCell";

		private static ModLog _log;

		public static GameObject ButtonTemplate { get; private set; }

		public static GameObject SettingsCellTemplate { get; private set; }

		public static bool IsReady => (Object)(object)ButtonTemplate != (Object)null;

		public static void SetLogger(ModLog log)
		{
			_log = log;
		}

		public static bool TryCapture()
		{
			if (IsReady)
			{
				return true;
			}
			try
			{
				GameObject[] source = Resources.FindObjectsOfTypeAll<GameObject>();
				GameObject val = ((IEnumerable<GameObject>)source).FirstOrDefault((Func<GameObject, bool>)((GameObject g) => (Object)(object)g != (Object)null && ((Object)g).name == "UI_MainMenuButton_LeaveGame (2)"));
				GameObject val2 = ((IEnumerable<GameObject>)source).FirstOrDefault((Func<GameObject, bool>)((GameObject g) => (Object)(object)g != (Object)null && ((Object)g).name == "SettingsCell"));
				if ((Object)(object)val == (Object)null)
				{
					ModLog log = _log;
					if (log != null)
					{
						log.Debug((object)"PeakStyle: button template not present yet.");
					}
					return false;
				}
				ButtonTemplate = Object.Instantiate<GameObject>(val);
				((Object)ButtonTemplate).name = "SpongeUI_ButtonTemplate";
				StripTemplate(ButtonTemplate);
				Object.DontDestroyOnLoad((Object)(object)ButtonTemplate);
				ButtonTemplate.SetActive(false);
				if ((Object)(object)val2 != (Object)null)
				{
					SettingsCellTemplate = Object.Instantiate<GameObject>(val2);
					((Object)SettingsCellTemplate).name = "SpongeUI_SettingsCellTemplate";
					StripTemplate(SettingsCellTemplate);
					Object.DontDestroyOnLoad((Object)(object)SettingsCellTemplate);
					SettingsCellTemplate.SetActive(false);
				}
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Info((object)"PeakStyle: captured UI templates from the game.");
				}
				return true;
			}
			catch (Exception ex)
			{
				ModLog log3 = _log;
				if (log3 != null)
				{
					log3.Exception("PeakStyle: failed to capture UI templates", ex);
				}
				return false;
			}
		}

		private static void StripTemplate(GameObject template)
		{
			LocalizedText[] componentsInChildren = template.GetComponentsInChildren<LocalizedText>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			Button[] componentsInChildren2 = template.GetComponentsInChildren<Button>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				((UnityEventBase)componentsInChildren2[i].onClick).RemoveAllListeners();
			}
		}

		public static Button CreateButton(string label, Transform parent, Action onClick)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			if (!TryCapture() || (Object)(object)parent == (Object)null)
			{
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(ButtonTemplate, parent, false);
			((Object)obj).name = "SpongeUI_Button_" + label;
			obj.SetActive(true);
			SetLabel(obj, label);
			Button componentInChildren = obj.GetComponentInChildren<Button>(true);
			if ((Object)(object)componentInChildren != (Object)null && onClick != null)
			{
				((UnityEvent)componentInChildren.onClick).AddListener((UnityAction)delegate
				{
					try
					{
						onClick();
					}
					catch (Exception ex)
					{
						ModLog log = _log;
						if (log != null)
						{
							log.Exception("Button '" + label + "' threw", ex);
						}
					}
				});
			}
			return componentInChildren;
		}

		public static void SetLabel(GameObject instance, string label)
		{
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			TextMeshProUGUI componentInChildren = instance.GetComponentInChildren<TextMeshProUGUI>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				((TMP_Text)componentInChildren).text = label;
				return;
			}
			Text componentInChildren2 = instance.GetComponentInChildren<Text>(true);
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				componentInChildren2.text = label;
			}
		}

		public static void Reset()
		{
			if ((Object)(object)ButtonTemplate != (Object)null)
			{
				Object.Destroy((Object)(object)ButtonTemplate);
			}
			if ((Object)(object)SettingsCellTemplate != (Object)null)
			{
				Object.Destroy((Object)(object)SettingsCellTemplate);
			}
			ButtonTemplate = null;
			SettingsCellTemplate = null;
		}
	}
	public static class ScrollableList
	{
		public static ScrollRect Ensure(Transform content, ModLog log = null, float spacing = 4f)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RectTransform val = (RectTransform)(object)((content is RectTransform) ? content : null);
				if (val == null)
				{
					return null;
				}
				ScrollRect componentInParent = ((Component)content).GetComponentInParent<ScrollRect>();
				if ((Object)(object)componentInParent != (Object)null)
				{
					return componentInParent;
				}
				Transform parent = ((Transform)val).parent;
				RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
				if ((Object)(object)val2 == (Object)null)
				{
					return null;
				}
				GameObject val3 = new GameObject("SpongeUI_Viewport", new Type[3]
				{
					typeof(RectTransform),
					typeof(RectMask2D),
					typeof(ScrollRect)
				});
				RectTransform component = val3.GetComponent<RectTransform>();
				((Transform)component).SetParent((Transform)(object)val2, false);
				((Transform)component).SetSiblingIndex(((Transform)val).GetSiblingIndex());
				component.anchorMin = val.anchorMin;
				component.anchorMax = val.anchorMax;
				component.pivot = val.pivot;
				component.anchoredPosition = val.anchoredPosition;
				component.sizeDelta = val.sizeDelta;
				component.offsetMin = val.offsetMin;
				component.offsetMax = val.offsetMax;
				((Transform)val).SetParent((Transform)(object)component, false);
				val.anchorMin = new Vector2(0f, 1f);
				val.anchorMax = new Vector2(1f, 1f);
				val.pivot = new Vector2(0.5f, 1f);
				val.anchoredPosition = Vector2.zero;
				val.offsetMin = new Vector2(0f, val.offsetMin.y);
				val.offsetMax = new Vector2(0f, val.offsetMax.y);
				if ((Object)(object)((Component)val).GetComponent<LayoutGroup>() == (Object)null)
				{
					VerticalLayoutGroup obj = ((Component)val).gameObject.AddComponent<VerticalLayoutGroup>();
					((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = false;
					((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
					((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
					((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
					((HorizontalOrVerticalLayoutGroup)obj).spacing = spacing;
				}
				ContentSizeFitter obj2 = ((Component)val).GetComponent<ContentSizeFitter>() ?? ((Component)val).gameObject.AddComponent<ContentSizeFitter>();
				obj2.verticalFit = (FitMode)2;
				obj2.horizontalFit = (FitMode)0;
				ScrollRect component2 = val3.GetComponent<ScrollRect>();
				component2.content = val;
				component2.viewport = component;
				component2.horizontal = false;
				component2.vertical = true;
				component2.movementType = (MovementType)2;
				component2.scrollSensitivity = 30f;
				component2.inertia = false;
				if (log != null)
				{
					log.Debug((object)"Added scrolling to a list.");
				}
				return component2;
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.Exception("Could not make a list scrollable", ex);
				}
				return null;
			}
		}

		public static void Refresh(ScrollRect scroll)
		{
			try
			{
				if (!((Object)(object)((scroll != null) ? scroll.content : null) == (Object)null))
				{
					LayoutRebuilder.ForceRebuildLayoutImmediate(scroll.content);
					Canvas.ForceUpdateCanvases();
					if (scroll.vertical)
					{
						scroll.verticalNormalizedPosition = 1f;
					}
				}
			}
			catch (Exception)
			{
			}
		}
	}
}
namespace SpongePEAK.Lib.Plugin
{
	public abstract class SpongePlugin : BaseUnityPlugin
	{
		public ModLog Log { get; private set; }

		public PatchHost Patches { get; private set; }

		public bool LoadedSuccessfully { get; private set; }

		protected virtual string DisplayName => ((object)this).GetType().Name;

		protected abstract void OnLoad();

		protected virtual void OnUnload()
		{
		}

		private void Awake()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			Log = new ModLog(DisplayName);
			Log.Attach(((BaseUnityPlugin)this).Logger);
			PluginInfo info = ((BaseUnityPlugin)this).Info;
			object obj;
			if (info == null)
			{
				obj = null;
			}
			else
			{
				BepInPlugin metadata = info.Metadata;
				obj = ((metadata != null) ? metadata.GUID : null);
			}
			if (obj == null)
			{
				obj = DisplayName;
			}
			Patches = new PatchHost((string)obj, Log);
			HudRoot.SetLogger(Log);
			MenuIntegration.SetLogger(Log);
			MenuIntegration.Install(Patches.Harmony);
			ModSettingsTab.SetLogger(Log);
			ModSettingsTab.Install(Patches.Harmony);
			ItemCloner.SetLogger(Log);
			ItemEffectPatches.Install(Patches.Harmony, Log);
			try
			{
				OnLoad();
				LoadedSuccessfully = true;
				Log.Info((object)(DisplayName + " loaded."));
			}
			catch (Exception ex)
			{
				LoadedSuccessfully = false;
				Log.Exception(DisplayName + " failed to load", ex);
			}
		}

		private void OnDestroy()
		{
			try
			{
				OnUnload();
			}
			catch (Exception ex)
			{
				ModLog log = Log;
				if (log != null)
				{
					log.Exception(DisplayName + " threw during unload", ex);
				}
			}
			PatchHost patches = Patches;
			if (patches != null)
			{
				patches.Dispose();
			}
		}
	}
}
namespace SpongePEAK.Lib.Platform
{
	public enum HostPlatform
	{
		Unknown,
		Windows,
		Proton,
		Linux,
		MacOS
	}
	public static class PlatformInfo
	{
		private static bool _detected;

		private static HostPlatform _platform;

		private static string _detail;

		public static HostPlatform Current
		{
			get
			{
				Detect();
				return _platform;
			}
		}

		public static string Detail
		{
			get
			{
				Detect();
				return _detail;
			}
		}

		public static bool IsProton => Current == HostPlatform.Proton;

		public static bool IsNativeWindows => Current == HostPlatform.Windows;

		public static bool IsTranslated => Current == HostPlatform.Proton;

		private static void Detect()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Invalid comparison between Unknown and I4
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Invalid comparison between Unknown and I4
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if (_detected)
			{
				return;
			}
			_detected = true;
			_platform = HostPlatform.Unknown;
			_detail = "undetermined";
			try
			{
				RuntimePlatform platform = Application.platform;
				if ((int)platform > 1)
				{
					if ((int)platform == 13 || (int)platform == 16)
					{
						_platform = HostPlatform.Linux;
						_detail = "native Linux build";
						return;
					}
					if ((int)Application.platform != 2 && (int)Application.platform != 7)
					{
						_detail = $"unrecognised platform {Application.platform}";
						return;
					}
					bool flag = false;
					try
					{
						flag = PlatformHelper.Is((Platform)131072);
					}
					catch (Exception)
					{
					}
					if (flag)
					{
						_platform = HostPlatform.Proton;
						_detail = DescribeProton();
					}
					else
					{
						_platform = HostPlatform.Windows;
						_detail = "native Windows (no Wine markers)";
					}
				}
				else
				{
					_platform = HostPlatform.MacOS;
					_detail = "native macOS build";
				}
			}
			catch (Exception)
			{
				_platform = HostPlatform.Unknown;
				_detail = "detection failed";
			}
		}

		private static string DescribeProton()
		{
			try
			{
				if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("STEAM_COMPAT_DATA_PATH")))
				{
					return "Proton (Steam compatibility layer)";
				}
				if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WINEPREFIX")))
				{
					return "Wine (WINEPREFIX set)";
				}
				return "Wine/Proton (ntdll wine marker)";
			}
			catch (Exception)
			{
				return "Wine/Proton (ntdll wine marker)";
			}
		}
	}
}
namespace SpongePEAK.Lib.Net
{
	public sealed class HostRuleSet : IInRoomCallbacks, IMatchmakingCallbacks
	{
		private readonly string _propertyKey;

		private readonly ModLog _log;

		private readonly Dictionary<string, bool> _defaults = new Dictionary<string, bool>(StringComparer.Ordinal);

		private readonly Dictionary<string, bool> _values = new Dictionary<string, bool>(StringComparer.Ordinal);

		private bool _registered;

		public static bool CanEdit
		{
			get
			{
				if (PhotonNetwork.InRoom)
				{
					return PhotonNetwork.IsMasterClient;
				}
				return true;
			}
		}

		public event Action Changed;

		public HostRuleSet(string propertyKey, ModLog log)
		{
			if (string.IsNullOrEmpty(propertyKey))
			{
				throw new ArgumentException("A room property key is required.", "propertyKey");
			}
			_propertyKey = propertyKey;
			_log = log;
		}

		public void Declare(string id, bool defaultValue)
		{
			if (!string.IsNullOrEmpty(id))
			{
				_defaults[id] = defaultValue;
			}
		}

		public bool IsOn(string id)
		{
			if (_values.TryGetValue(id, out var value))
			{
				return value;
			}
			bool value2;
			return _defaults.TryGetValue(id, out value2) && value2;
		}

		public bool Set(string id, bool value)
		{
			if (!_defaults.ContainsKey(id))
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Warning((object)("Ignored unknown host rule '" + id + "'."));
				}
				return false;
			}
			if (!CanEdit)
			{
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Debug((object)("Refused to set '" + id + "': not the host."));
				}
				return false;
			}
			_values[id] = value;
			this.Changed?.Invoke();
			Publish();
			return true;
		}

		public void Start()
		{
			if (!_registered)
			{
				PhotonNetwork.AddCallbackTarget((object)this);
				_registered = true;
			}
		}

		public void Stop()
		{
			if (_registered)
			{
				PhotonNetwork.RemoveCallbackTarget((object)this);
				_registered = false;
			}
		}

		public void Publish()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0028: Expected O, but got Unknown
			if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				Hashtable val = new Hashtable();
				((Dictionary<object, object>)val).Add((object)_propertyKey, (object)Serialize());
				Hashtable val2 = val;
				PhotonNetwork.CurrentRoom.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null);
				ModLog log = _log;
				if (log != null)
				{
					log.Debug((object)$"Published {_values.Count} host rule(s).");
				}
			}
			catch (Exception ex)
			{
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Exception("Could not publish host rules", ex);
				}
			}
		}

		public bool ApplyFromRoom()
		{
			if (PhotonNetwork.InRoom)
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null) != null)
				{
					if (((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)_propertyKey, out object value) && value is string payload)
					{
						return ApplyRemote(payload);
					}
					return ApplyRemote(string.Empty);
				}
			}
			return false;
		}

		private bool ApplyRemote(string payload)
		{
			Dictionary<string, bool> dictionary = Deserialize(payload);
			bool flag = dictionary.Count == _values.Count;
			if (flag)
			{
				foreach (KeyValuePair<string, bool> item in dictionary)
				{
					if (!_values.TryGetValue(item.Key, out var value) || value != item.Value)
					{
						flag = false;
						break;
					}
				}
			}
			if (flag)
			{
				return false;
			}
			_values.Clear();
			foreach (KeyValuePair<string, bool> item2 in dictionary)
			{
				_values[item2.Key] = item2.Value;
			}
			ModLog log = _log;
			if (log != null)
			{
				log.Info((object)$"Applied {_values.Count} host rule(s).");
			}
			this.Changed?.Invoke();
			return true;
		}

		private string Serialize()
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (KeyValuePair<string, bool> value2 in _values)
			{
				if (!_defaults.TryGetValue(value2.Key, out var value) || value != value2.Value)
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(';');
					}
					stringBuilder.Append(value2.Key).Append('=').Append(value2.Value ? '1' : '0');
				}
			}
			return stringBuilder.ToString();
		}

		private Dictionary<string, bool> Deserialize(string payload)
		{
			Dictionary<string, bool> dictionary = new Dictionary<string, bool>(StringComparer.Ordinal);
			if (string.IsNullOrEmpty(payload))
			{
				return dictionary;
			}
			string[] array = payload.Split(';');
			foreach (string text in array)
			{
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				int num = text.IndexOf('=');
				if (num > 0 && num < text.Length - 1)
				{
					string key = text.Substring(0, num);
					if (_defaults.ContainsKey(key))
					{
						dictionary[key] = text[num + 1] == '1';
					}
				}
			}
			return dictionary;
		}

		public IEnumerable<KeyValuePair<string, bool>> All()
		{
			foreach (KeyValuePair<string, bool> @default in _defaults)
			{
				yield return new KeyValuePair<string, bool>(@default.Key, IsOn(@default.Key));
			}
		}

		public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
		{
			if (propertiesThatChanged != null && ((Dictionary<object, object>)(object)propertiesThatChanged).ContainsKey((object)_propertyKey) && !PhotonNetwork.IsMasterClient)
			{
				ApplyFromRoom();
			}
		}

		public void OnJoinedRoom()
		{
			ApplyFromRoom();
		}

		public void OnMasterClientSwitched(Player newMasterClient)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				ApplyFromRoom();
				Publish();
				ModLog log = _log;
				if (log != null)
				{
					log.Info((object)"Promoted to host; keeping the lobby's current rules.");
				}
			}
		}

		public void OnPlayerEnteredRoom(Player newPlayer)
		{
		}

		public void OnPlayerLeftRoom(Player otherPlayer)
		{
		}

		public void OnPlayerPropertiesUpdate(Player target, Hashtable changedProps)
		{
		}

		public void OnCreatedRoom()
		{
		}

		public void OnCreateRoomFailed(short returnCode, string message)
		{
		}

		public void OnJoinRoomFailed(short returnCode, string message)
		{
		}

		public void OnJoinRandomFailed(short returnCode, string message)
		{
		}

		public void OnLeftRoom()
		{
			_values.Clear();
		}

		public void OnFriendListUpdate(List<FriendInfo> friendList)
		{
		}
	}
	public enum LinkTier
	{
		Unknown,
		Good,
		Fair,
		Poor,
		Bad
	}
	public sealed class LinkQuality
	{
		private const int WindowSize = 12;

		private const int ChangeThreshold = 3;

		private readonly int[] _rtt = new int[12];

		private readonly int[] _jitter = new int[12];

		private int _count;

		private int _next;

		private int _lastResends;

		private int _resendDelta;

		private long _lastBytesIn;

		private long _lastBytesOut;

		private float _lastThroughputTime;

		private bool _throughputBaselineSet;

		private bool _resendBaselineSet;

		private LinkTier _tier;

		private LinkTier _pending;

		private int _pendingCount;

		private readonly ModLog _log;

		public LinkTier Tier => _tier;

		public int SmoothedRtt { get; private set; }

		public int SmoothedJitter { get; private set; }

		public int RecentResends => _resendDelta;

		public int QueuedOutgoing { get; private set; }

		public int UploadBytesPerSecond { get; private set; }

		public int DownloadBytesPerSecond { get; private set; }

		public event Action<LinkTier> TierChanged;

		public LinkQuality(ModLog log)
		{
			_log = log;
		}

		public void Sample()
		{
			try
			{
				LoadBalancingClient networkingClient = PhotonNetwork.NetworkingClient;
				LoadBalancingPeer val = ((networkingClient != null) ? networkingClient.LoadBalancingPeer : null);
				if (val == null || !PhotonNetwork.IsConnected)
				{
					return;
				}
				int roundTripTime = ((PhotonPeer)val).RoundTripTime;
				int roundTripTimeVariance = ((PhotonPeer)val).RoundTripTimeVariance;
				if (roundTripTime > 0)
				{
					_rtt[_next] = roundTripTime;
					_jitter[_next] = roundTripTimeVariance;
					_next = (_next + 1) % 12;
					if (_count < 12)
					{
						_count++;
					}
					SmoothedRtt = Average(_rtt);
					SmoothedJitter = Average(_jitter);
					int resentReliableCommands = ((PhotonPeer)val).ResentReliableCommands;
					if (!_resendBaselineSet)
					{
						_lastResends = resentReliableCommands;
						_resendBaselineSet = true;
						_resendDelta = 0;
					}
					else if (resentReliableCommands < _lastResends)
					{
						_lastResends = resentReliableCommands;
						_resendDelta = 0;
					}
					else
					{
						_resendDelta = resentReliableCommands - _lastResends;
						_lastResends = resentReliableCommands;
					}
					QueuedOutgoing = ((PhotonPeer)val).QueuedOutgoingCommands;
					SampleThroughput((PhotonPeer)(object)val);
					Evaluate(Classify());
				}
			}
			catch (Exception ex)
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Exception("Link quality sampling failed", ex);
				}
			}
		}

		private void SampleThroughput(PhotonPeer peer)
		{
			try
			{
				if (!peer.TrafficStatsEnabled)
				{
					peer.TrafficStatsEnabled = true;
				}
				long bytesIn = peer.BytesIn;
				long bytesOut = peer.BytesOut;
				float unscaledTime = Time.unscaledTime;
				if (!_throughputBaselineSet)
				{
					_lastBytesIn = bytesIn;
					_lastBytesOut = bytesOut;
					_lastThroughputTime = unscaledTime;
					_throughputBaselineSet = true;
					return;
				}
				float num = unscaledTime - _lastThroughputTime;
				if (!(num < 0.5f))
				{
					if (bytesIn < _lastBytesIn || bytesOut < _lastBytesOut)
					{
						_lastBytesIn = bytesIn;
						_lastBytesOut = bytesOut;
						_lastThroughputTime = unscaledTime;
					}
					else
					{
						DownloadBytesPerSecond = (int)((float)(bytesIn - _lastBytesIn) / num);
						UploadBytesPerSecond = (int)((float)(bytesOut - _lastBytesOut) / num);
						_lastBytesIn = bytesIn;
						_lastBytesOut = bytesOut;
						_lastThroughputTime = unscaledTime;
					}
				}
			}
			catch (Exception ex)
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Exception("Throughput sampling failed", ex);
				}
			}
		}

		private LinkTier Classify()
		{
			if (_count < 3)
			{
				return LinkTier.Unknown;
			}
			int smoothedRtt = SmoothedRtt;
			int smoothedJitter = SmoothedJitter;
			if (_resendDelta >= 8 || QueuedOutgoing >= 24)
			{
				return LinkTier.Bad;
			}
			if (smoothedRtt >= 400 || smoothedJitter >= 150)
			{
				return LinkTier.Bad;
			}
			if (smoothedRtt >= 220 || smoothedJitter >= 90 || _resendDelta >= 3)
			{
				return LinkTier.Poor;
			}
			if (smoothedRtt >= 110 || smoothedJitter >= 45 || _resendDelta >= 1)
			{
				return LinkTier.Fair;
			}
			return LinkTier.Good;
		}

		private void Evaluate(LinkTier candidate)
		{
			if (candidate == LinkTier.Unknown)
			{
				return;
			}
			if (candidate == _tier)
			{
				_pendingCount = 0;
				return;
			}
			if (candidate == _pending)
			{
				_pendingCount++;
			}
			else
			{
				_pending = candidate;
				_pendingCount = 1;
			}
			int num = ((candidate > _tier) ? 2 : 3);
			if (_pendingCount >= num)
			{
				LinkTier tier = _tier;
				_tier = candidate;
				_pendingCount = 0;
				ModLog log = _log;
				if (log != null)
				{
					log.Info((object)($"Connection quality {tier} -> {_tier} " + $"(rtt {SmoothedRtt}ms, jitter {SmoothedJitter}ms, " + $"resends {_resendDelta}, queued {QueuedOutgoing})."));
				}
				this.TierChanged?.Invoke(_tier);
			}
		}

		private int Average(int[] values)
		{
			if (_count == 0)
			{
				return 0;
			}
			long num = 0L;
			for (int i = 0; i < _count; i++)
			{
				num += values[i];
			}
			return (int)(num / _count);
		}

		public override string ToString()
		{
			return $"{_tier} (rtt {SmoothedRtt}ms, jitter {SmoothedJitter}ms)";
		}
	}
	public sealed class LinkReporter : IInRoomCallbacks, IMatchmakingCallbacks
	{
		public const string PropertyKey = "sponge.link";

		private readonly LinkQuality _quality;

		private readonly ModLog _log;

		private bool _registered;

		private LinkTier _published;

		public event Action Changed;

		public LinkReporter(LinkQuality quality, ModLog log)
		{
			_quality = quality;
			_log = log;
		}

		public void Start()
		{
			if (!_registered)
			{
				PhotonNetwork.AddCallbackTarget((object)this);
				_registered = true;
				if (_quality != null)
				{
					_quality.TierChanged += OnLocalTierChanged;
				}
			}
		}

		public void Stop()
		{
			if (_registered)
			{
				PhotonNetwork.RemoveCallbackTarget((object)this);
				_registered = false;
				if (_quality != null)
				{
					_quality.TierChanged -= OnLocalTierChanged;
				}
			}
		}

		private void OnLocalTierChanged(LinkTier tier)
		{
			Publish();
		}

		public void Publish()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0042: Expected O, but got Unknown
			if (!PhotonNetwork.InRoom || _quality == null)
			{
				return;
			}
			LinkTier tier = _quality.Tier;
			if (tier == LinkTier.Unknown || tier == _published)
			{
				return;
			}
			try
			{
				Hashtable val = new Hashtable();
				((Dictionary<object, object>)val).Add((object)"sponge.link", (object)(byte)tier);
				Hashtable val2 = val;
				PhotonNetwork.LocalPlayer.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null);
				_published = tier;
				ModLog log = _log;
				if (log != null)
				{
					log.Debug((object)$"Published link tier {tier}.");
				}
			}
			catch (Exception ex)
			{
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Exception("Could not publish link tier", ex);
				}
			}
		}

		public static LinkTier TierOf(Player player)
		{
			try
			{
				if (((player != null) ? player.CustomProperties : null) != null && ((Dictionary<object, object>)(object)player.CustomProperties).TryGetValue((object)"sponge.link", out object value) && value is byte b && Enum.IsDefined(typeof(LinkTier), (int)b))
				{
					return (LinkTier)b;
				}
			}
			catch (Exception)
			{
			}
			return LinkTier.Unknown;
		}

		public static LinkTier WorstReported()
		{
			LinkTier linkTier = LinkTier.Unknown;
			try
			{
				Player[] playerList = PhotonNetwork.PlayerList;
				if (playerList == null)
				{
					return linkTier;
				}
				Player[] array = playerList;
				for (int i = 0; i < array.Length; i++)
				{
					LinkTier linkTier2 = TierOf(array[i]);
					if (linkTier2 != LinkTier.Unknown && linkTier2 > linkTier)
					{
						linkTier = linkTier2;
					}
				}
				return linkTier;
			}
			catch (Exception)
			{
				return LinkTier.Unknown;
			}
		}

		public static IEnumerable<KeyValuePair<string, LinkTier>> All()
		{
			Player[] playerList = PhotonNetwork.PlayerList;
			if (playerList != null)
			{
				Player[] array = playerList;
				foreach (Player val in array)
				{
					yield return new KeyValuePair<string, LinkTier>(val.NickName ?? "?", TierOf(val));
				}
			}
		}

		public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
		{
			if (changedProps != null && ((Dictionary<object, object>)(object)changedProps).ContainsKey((object)"sponge.link"))
			{
				this.Changed?.Invoke();
			}
		}

		public void OnJoinedRoom()
		{
			_published = LinkTier.Unknown;
			Publish();
		}

		public void OnPlayerEnteredRoom(Player newPlayer)
		{
			this.Changed?.Invoke();
		}

		public void OnPlayerLeftRoom(Player otherPlayer)
		{
			this.Changed?.Invoke();
		}

		public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
		{
		}

		public void OnMasterClientSwitched(Player newMasterClient)
		{
		}

		public void OnCreatedRoom()
		{
		}

		public void OnCreateRoomFailed(short returnCode, string message)
		{
		}

		public void OnJoinRoomFailed(short returnCode, string message)
		{
		}

		public void OnJoinRandomFailed(short returnCode, string message)
		{
		}

		public void OnLeftRoom()
		{
			_published = LinkTier.Unknown;
		}

		public void OnFriendListUpdate(List<FriendInfo> friendList)
		{
		}
	}
	public sealed class ModHandshake : IInRoomCallbacks, IMatchmakingCallbacks
	{
		public const string PropertyKey = "sponge.mods";

		private const char Separator = ';';

		private readonly ModLog _log;

		private readonly HashSet<string> _localMods = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<int, HashSet<string>> _remoteMods = new Dictionary<int, HashSet<string>>();

		private bool _registered;

		public IReadOnlyCollection<string> LocalMods => _localMods;

		public event Action<Player> PeerModsChanged;

		public ModHandshake(ModLog log)
		{
			_log = log;
		}

		public void Start()
		{
			CollectLocalMods();
			if (!_registered)
			{
				PhotonNetwork.AddCallbackTarget((object)this);
				_registered = true;
			}
			Publish();
		}

		public void Stop()
		{
			if (_registered)
			{
				PhotonNetwork.RemoveCallbackTarget((object)this);
				_registered = false;
			}
			_remoteMods.Clear();
		}

		private void CollectLocalMods()
		{
			_localMods.Clear();
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					PluginInfo value = pluginInfo.Value;
					object obj;
					if (value == null)
					{
						obj = null;
					}
					else
					{
						BepInPlugin metadata = value.Metadata;
						obj = ((metadata != null) ? metadata.GUID : null);
					}
					string text = (string)obj;
					if (!string.IsNullOrEmpty(text))
					{
						_localMods.Add(text);
					}
				}
			}
			catch (Exception ex)
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Exception("Could not enumerate loaded plugins", ex);
				}
			}
			ModLog log2 = _log;
			if (log2 != null)
			{
				log2.Debug((object)$"Advertising {_localMods.Count} loaded mod(s).");
			}
		}

		public void Publish()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_0038: Expected O, but got Unknown
			if (!PhotonNetwork.InRoom || PhotonNetwork.LocalPlayer == null)
			{
				return;
			}
			try
			{
				string value = string.Join(';'.ToString(), _localMods);
				Hashtable val = new Hashtable();
				((Dictionary<object, object>)val).Add((object)"sponge.mods", (object)value);
				Hashtable val2 = val;
				PhotonNetwork.LocalPlayer.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null);
			}
			catch (Exception ex)
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Exception("Failed to publish the local mod list", ex);
				}
			}
		}

		public IReadOnlyCollection<string> GetMods(Player player)
		{
			if (player == null)
			{
				return (IReadOnlyCollection<string>)(object)Array.Empty<string>();
			}
			if (player.IsLocal)
			{
				return _localMods;
			}
			if (_remoteMods.TryGetValue(player.ActorNumber, out var value))
			{
				return value;
			}
			HashSet<string> hashSet = Parse(player);
			_remoteMods[player.ActorNumber] = hashSet;
			return hashSet;
		}

		public bool PlayerHasMod(Player player, string guid)
		{
			if (string.IsNullOrEmpty(guid))
			{
				return false;
			}
			foreach (string mod in GetMods(player))
			{
				if (string.Equals(mod, guid, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		public bool HostHasMod(string guid)
		{
			if (PhotonNetwork.OfflineMode || !PhotonNetwork.InRoom)
			{
				return true;
			}
			Player masterClient = PhotonNetwork.MasterClient;
			if (masterClient == null)
			{
				return false;
			}
			if (masterClient.IsLocal)
			{
				return _localMods.Contains(guid);
			}
			return PlayerHasMod(masterClient, guid);
		}

		public bool EveryoneHasMod(string guid)
		{
			if (!PhotonNetwork.InRoom)
			{
				return false;
			}
			Player[] playerList = PhotonNetwork.PlayerList;
			if (playerList == null || playerList.Length == 0)
			{
				return false;
			}
			Player[] array = playerList;
			foreach (Player player in array)
			{
				if (!PlayerHasMod(player, guid))
				{
					return false;
				}
			}
			return true;
		}

		public IEnumerable<Player> PlayersWithMod(string guid)
		{
			if (PhotonNetwork.PlayerList == null)
			{
				yield break;
			}
			Player[] playerList = PhotonNetwork.PlayerList;
			foreach (Player val in playerList)
			{
				if (PlayerHasMod(val, guid))
				{
					yield return val;
				}
			}
		}

		private HashSet<string> Parse(Player player)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			try
			{
				if (player.CustomProperties != null && ((Dictionary<object, object>)(object)player.CustomProperties).TryGetValue((object)"sponge.mods", out object value) && value is string { Length: >0 } text)
				{
					string[] array = text.Split(';');
					foreach (string text2 in array)
					{
						if (!string.IsNullOrEmpty(text2))
						{
							hashSet.Add(text2);
						}
					}
				}
			}
			catch (Exception ex)
			{
				ModLog log = _log;
				if (log != null)
				{
					log.Exception("Could not parse the mod list for '" + player.NickName + "'", ex);
				}
			}
			return hashSet;
		}

		public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
		{
			if (targetPlayer == null || changedProps == null || !((Dictionary<object, object>)(object)changedProps).ContainsKey((object)"sponge.mods"))
			{
				return;
			}
			_remoteMods[targetPlayer.ActorNumber] = Parse(targetPlayer);
			ModLog log = _log;
			if (log != null)
			{
				log.Debug((object)$"'{targetPlayer.NickName}' advertises {_remoteMods[targetPlayer.ActorNumber].Count} mod(s).");
			}
			try
			{
				this.PeerModsChanged?.Invoke(targetPlayer);
			}
			catch (Exception ex)
			{
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Exception("A PeerModsChanged subscriber threw", ex);
				}
			}
		}

		public void OnPlayerLeftRoom(Player otherPlayer)
		{
			if (otherPlayer != null)
			{
				_remoteMods.Remove(otherPlayer.ActorNumber);
			}
		}

		public void OnJoinedRoom()
		{
			Publish();
		}

		public void OnPlayerEnteredRoom(Player newPlayer)
		{
		}

		public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
		{
		}

		public void OnMasterClientSwitched(Player newMasterClient)
		{
		}

		public void OnFriendListUpdate(List<FriendInfo> friendList)
		{
		}

		public void OnCreatedRoom()
		{
		}

		public void OnCreateRoomFailed(short returnCode, string message)
		{
		}

		public void OnJoinRoomFailed(short returnCode, string message)
		{
		}

		public void OnJoinRandomFailed(short returnCode, string message)
		{
		}

		public void OnLeftRoom()
		{
			_remoteMods.Clear();
		}
	}
	public static class NetInfo
	{
		public static bool IsConnected => PhotonNetwork.IsConnected;

		public static bool InRoom => PhotonNetwork.InRoom;

		public static bool IsOffline => PhotonNetwork.OfflineMode;

		public static bool IsHost
		{
			get
			{
				if (!PhotonNetwork.OfflineMode && PhotonNetwork.InRoom)
				{
					return PhotonNetwork.IsMasterClient;
				}
				return true;
			}
		}

		public static Player LocalPlayer => PhotonNetwork.LocalPlayer;

		public static Room CurrentRoom => PhotonNetwork.CurrentRoom;

		public static int PlayerCount
		{
			get
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom == null)
				{
					return 0;
				}
				return currentRoom.PlayerCount;
			}
		}

		public static int MaxPlayers
		{
			get
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom == null)
				{
					return 0;
				}
				return currentRoom.MaxPlayers;
			}
		}

		public static bool IsMultiplayer
		{
			get
			{
				if (InRoom)
				{
					return PlayerCount > 1;
				}
				return false;
			}
		}
	}
	public enum NetRole
	{
		Offline,
		Client,
		Host,
		Solo
	}
	public static class NetRoleInfo
	{
		public static NetRole Current
		{
			get
			{
				if (PhotonNetwork.OfflineMode)
				{
					return NetRole.Solo;
				}
				if (!PhotonNetwork.InRoom)
				{
					return NetRole.Offline;
				}
				if (PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.PlayerCount <= 1)
				{
					return NetRole.Solo;
				}
				if (!PhotonNetwork.IsMasterClient)
				{
					return NetRole.Client;
				}
				return NetRole.Host;
			}
		}

		public static bool HasAuthority => Current != NetRole.Client;

		public static bool CanActLocally => true;

		public static bool IsMultiplayer
		{
			get
			{
				NetRole current = Current;
				if (current != NetRole.Host)
				{
					return current == NetRole.Client;
				}
				return true;
			}
		}

		public static bool IsHost => Current == NetRole.Host;

		public static bool IsClient => Current == NetRole.Client;
	}
	public static class PlayerUtil
	{
		public const string AirportScene = "Airport";

		public static bool InAirport
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				Scene activeScene = SceneManager.GetActiveScene();
				return ((Scene)(ref activeScene)).name == "Airport";
			}
		}

		public static string GetStableKey(Player player)
		{
			if (player == null)
			{
				return string.Empty;
			}
			if (!string.IsNullOrEmpty(player.UserId))
			{
				return "USERID:" + player.UserId;
			}
			if (!string.IsNullOrEmpty(player.NickName))
			{
				return "NICK:" + player.NickName;
			}
			return "ACTOR:" + player.ActorNumber;
		}

		public static Character FindFurthestFromSummit(Character exclude = null)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			Character result = null;
			float num = float.MaxValue;
			List<Character> allCharacters = GameState.AllCharacters;
			for (int i = 0; i < allCharacters.Count; i++)
			{
				Character val = allCharacters[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.data == (Object)null) && !((Object)(object)val == (Object)(object)exclude) && !val.data.dead)
				{
					float y = val.Center.y;
					if (y < num)
					{
						num = y;
						result = val;
					}
				}
			}
			return result;
		}

		public static IEnumerator WaitForCharacter(Player player, Action<Character> onReady, float timeout = 30f, float pollInterval = 0.1f)
		{
			float elapsed = 0f;
			Character character = null;
			for (; elapsed < timeout; elapsed += pollInterval)
			{
				character = GetCharacter(player);
				if ((Object)(object)character != (Object)null)
				{
					break;
				}
				yield return (object)new WaitForSeconds(pollInterval);
			}
			onReady?.Invoke(character);
		}

		public static Character GetCharacter(Player player)
		{
			if (player == null)
			{
				return null;
			}
			try
			{
				return PlayerHandler.GetPlayerCharacter(player);
			}
			catch
			{
				return null;
			}
		}

		public static bool Warp(Character character, Vector3 position)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)character == (Object)null || (Object)(object)((MonoBehaviourPun)character).photonView == (Object)null)
			{
				return false;
			}
			try
			{
				((MonoBehaviourPun)character).photonView.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { position, false });
				return true;
			}
			catch
			{
				return false;
			}
		}

		public static bool ReviveAt(Character character, Vector3 position)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)character == (Object)null || (Object)(object)((MonoBehaviourPun)character).photonView == (Object)null)
			{
				return false;
			}
			try
			{
				((MonoBehaviourPun)character).photonView.RPC("RPCA_ReviveAtPosition", (RpcTarget)0, new object[3] { position, false, -1 });
				return true;
			}
			catch
			{
				return false;
			}
		}
	}
}
namespace SpongePEAK.Lib.Mods
{
	public static class ModLocalization
	{
		private const string KeyPrefix = "SPONGE_MOD_";

		private const int LanguageColumns = 16;

		private static readonly Dictionary<string, string> Registered = new Dictionary<string, string>(StringComparer.Ordinal);

		public static string Register(string id, string text, ModLog log)
		{
			string text2 = Sanitize("SPONGE_MOD_" + id);
			try
			{
				if (Registered.TryGetValue(text2, out var value) && value == text)
				{
					return text2;
				}
				Dictionary<string, List<string>> mainTable = LocalizedText.mainTable;
				if (mainTable == null)
				{
					return text2;
				}
				List<string> list = new List<string>(16);
				for (int i = 0; i < 16; i++)
				{
					list.Add(text);
				}
				mainTable[text2] = list;
				Registered[text2] = text;
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.Exception("Could not register localisation for '" + text2 + "'", ex);
				}
			}
			return text2;
		}

		private static string Sanitize(string raw)
		{
			StringBuilder stringBuilder = new StringBuilder(raw.Length);
			foreach (char c in raw)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(char.ToUpperInvariant(c));
				}
				else
				{
					stringBuilder.Append('_');
				}
			}
			return stringBuilder.ToString();
		}
	}
	public sealed class ModSwitch
	{
		private readonly ConfigEntryBase _entry;

		public ModEntry Owner { get; }

		public string Section => _entry.Definition.Section;

		public string Key => _entry.Definition.Key;

		public string Description
		{
			get
			{
				ConfigDescription description = _entry.Description;
				return ((description != null) ? description.Description : null) ?? string.Empty;
			}
		}

		public string Id => Owner.Guid + "/" + Section + "/" + Key;

		public bool IsProtected => Owner.IsProtected;

		public bool Value
		{
			get
			{
				try
				{
					object boxedValue = _entry.BoxedValue;
					bool flag = default(bool);
					int num;
					if (boxedValue is bool)
					{
						flag = (bool)boxedValue;
						num = 1;
					}
					else
					{
						num = 0;
					}
					return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
				}
				catch
				{
					return false;
				}
			}
		}

		internal ModSwitch(ModEntry owner, ConfigEntryBase entry)
		{
			Owner = owner;
			_entry = entry;
		}

		public bool TrySet(bool value, ModLog log)
		{
			if (IsProtected)
			{
				if (log != null)
				{
					log.Warning((object)("Refused to change protected setting '" + Id + "'."));
				}
				return false;
			}
			try
			{
				_entry.BoxedValue = value;
				return true;
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.Exception("Failed to set '" + Id + "'", ex);
				}
				return false;
			}
		}
	}
	public sealed class ModEntry
	{
		private readonly List<ModSwitch> _switches = new List<ModSwitch>();

		public string Guid { get; }

		public string Name { get; }

		public string Version { get; }

		public bool IsProtected { get; }

		public string ProtectedReason { get; }

		public IReadOnlyList<ModSwitch> Switches => _switches;

		public bool HasSwitches => _switches.Count > 0;

		internal ModEntry(PluginInfo info, bool isProtected, string protectedReason)
		{
			object obj;
			if (info == null)
			{
				obj = null;
			}
			else
			{
				BepInPlugin metadata = info.Metadata;
				obj = ((metadata != null) ? metadata.GUID : null);
			}
			if (obj == null)
			{
				obj = "unknown";
			}
			Guid = (string)obj;
			object obj2;
			if (info == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata2 = info.Metadata;
				obj2 = ((metadata2 != null) ? metadata2.Name : null);
			}
			if (obj2 == null)
			{
				obj2 = Guid;
			}
			Name = (string)obj2;
			object obj3;
			if (info == null)
			{
				obj3 = null;
			}
			else
			{
				BepInPlugin metadata3 = info.Metadata;
				obj3 = ((metadata3 == null) ? null : metadata3.Version?.ToString());
			}
			if (obj3 == null)
			{
				obj3 = "?";
			}
			Version = (string)obj3;
			IsProtected = isProtected;
			ProtectedReason = protectedReason;
		}

		internal void Add(ModSwitch entry)
		{
			_switches.Add(entry);
		}
	}
	public static class ModRegistry
	{
		private static readonly (string Prefix, string Reason)[] BuiltInProtected = new(string, string)[2]
		{
			("com.sponge.peakcore", "Core library. Everything else depends on it."),
			("com.sponge.peaklib", "Provides this settings panel and the shared modding API.")
		};

		private static readonly List<(string Prefix, string Reason)> ExtraProtected = new List<(string, string)>();

		private static readonly string[] HiddenPrefixes = new string[4] { "bepinex.", "com.bepis.", "hamunii.", "monodetour." };

		private static readonly string[] HiddenExact = new string[1] { "bepinex" };

		public static void Protect(string guidPrefix, string reason)
		{
			if (string.IsNullOrEmpty(guidPrefix))
			{
				return;
			}
			foreach (var item in ExtraProtected)
			{
				if (string.Equals(item.Prefix, guidPrefix, StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
			}
			ExtraProtected.Add((guidPrefix, reason ?? "Required by another installed mod."));
		}

		public static IReadOnlyList<ModEntry> Discover(ModLog log)
		{
			List<ModEntry> list = new List<ModEntry>();
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					PluginInfo value = pluginInfo.Value;
					object obj;
					if (value == null)
					{
						obj = null;
					}
					else
					{
						BepInPlugin metadata = value.Metadata;
						obj = ((metadata != null) ? metadata.GUID : null);
					}
					string text = (string)obj;
					if (string.IsNullOrEmpty(text) || IsHidden(text))
					{
						continue;
					}
					BaseUnityPlugin instance = value.Instance;
					ConfigFile val = ((instance != null) ? instance.Config : null);
					if (val == null)
					{
						continue;
					}
					(bool, string) tuple = Classify(text);
					bool item = tuple.Item1;
					string item2 = tuple.Item2;
					ModEntry modEntry = new ModEntry(value, item, item2);
					foreach (ConfigDefinition key in val.Keys)
					{
						ConfigEntryBase val2;
						try
						{
							val2 = val[key];
						}
						catch
						{
							continue;
						}
						if (val2 != null && !(val2.SettingType != typeof(bool)))
						{
							modEntry.Add(new ModSwitch(modEntry, val2));
						}
					}
					if (modEntry.HasSwitches)
					{
						list.Add(modEntry);
					}
				}
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.Exception("Failed to enumerate installed mods", ex);
				}
			}
			return list.OrderByDescending((ModEntry m) => m.IsProtected).ThenBy<ModEntry, string>((ModEntry m) => m.Name, StringComparer.OrdinalIgnoreCase).ToList();
		}

		private static (bool, string) Classify(string guid)
		{
			(string, string)[] builtInProtected = BuiltInProtected;
			for (int i = 0; i < builtInProtected.Length; i++)
			{
				var (value, item) = builtInProtected[i];
				if (guid.StartsWith(value, StringComparison.OrdinalIgnoreCase))
				{
					return (true, item);
				}
			}
			foreach (var (value2, item2) in ExtraProtected)
			{
				if (guid.StartsWith(value2, StringComparison.OrdinalIgnoreCase))
				{
					return (true, item2);
				}
			}
			return (false, null);
		}

		private static bool IsHidden(string guid)
		{
			string[] hiddenExact = HiddenExact;
			foreach (string value in hiddenExact)
			{
				if (guid.Equals(value, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			hiddenExact = HiddenPrefixes;
			foreach (string value2 in hiddenExact)
			{
				if (guid.StartsWith(value2, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}
	}
	public static class ModRestartTracker
	{
		private static readonly HashSet<string> Changed = new HashSet<string>(StringComparer.Ordinal);

		public static bool AnyPending => Changed.Count > 0;

		public static int PendingCount => Changed.Count;

		public static void MarkChanged(string id)
		{
			if (!string.IsNullOrEmpty(id))
			{
				Changed.Add(id);
			}
		}

		public static bool NeedsRestart(string id)
		{
			if (!string.IsNullOrEmpty(id))
			{
				return Changed.Contains(id);
			}
			return false;
		}
	}
	public static class ModSettingsTab
	{
		private const string TabObjectName = "SpongeUI_ModsTab";

		private static ModLog _log;

		private static bool _installed;

		private static readonly Dictionary<string, ModToggleSetting> Registered = new Dictionary<string, ModToggleSetting>(StringComparer.Ordinal);

		internal static SettingsTABSButton ModTab { get; private set; }

		public static void SetLogger(ModLog log)
		{
			_log = log;
		}

		public static void Install(Harmony harmony)
		{
			if (_installed || harmony == null)
			{
				return;
			}
			try
			{
				harmony.PatchAll(typeof(ModSettingsPatches));
				_installed = true;
				ModLog log = _log;
				if (log != null)
				{
					log.Debug((object)"Mod settings tab hooks installed.");
				}
			}
			catch (Exception ex)
			{
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Exception("Failed to install the mod settings tab", ex);
				}
			}
		}

		internal static void SyncSettings()
		{
			GameHandler instance = GameHandler.Instance;
			SettingsHandler val = ((instance != null) ? instance.SettingsHandler : null);
			if (val == null)
			{
				return;
			}
			foreach (ModEntry item in ModRegistry.Discover(_log))
			{
				foreach (ModSwitch @switch in item.Switches)
				{
					string text = BuildLabel(item, @switch);
					string displayName = ModLocalization.Register(@switch.Id, text, _log);
					if (Registered.TryGetValue(@switch.Id, out var value))
					{
						((Setting)value).Load((ISettingsSaveLoad)null);
						continue;
					}
					ModToggleSetting modToggleSetting = new ModToggleSetting(@switch, displayName, _log);
					Registered[@switch.Id] = modToggleSetting;
					try
					{
						val.AddSetting((Setting)(object)modToggleSetting);
					}
					catch (Exception ex)
					{
						ModLog log = _log;
						if (log != null)
						{
							log.Exception("Could not register '" + @switch.Id + "' as a setting", ex);
						}
						Registered.Remove(@switch.Id);
					}
				}
			}
		}

		private static string BuildLabel(ModEntry mod, ModSwitch entry)
		{
			string text = entry.Key;
			if (!string.IsNullOrEmpty(entry.Section) && !entry.Section.Equals(mod.Name, StringComparison.OrdinalIgnoreCase) && !entry.Section.Equals(entry.Key, StringComparison.OrdinalIgnoreCase))
			{
				text = entry.Section + " / " + entry.Key;
			}
			string text2 = mod.Name + "  ·  " + text;
			if (mod.IsProtected)
			{
				text2 += (string.IsNullOrEmpty(mod.ProtectedReason) ? "   [required]" : ("   [required — " + mod.ProtectedReason + "]"));
			}
			else if (ModRestartTracker.NeedsRestart(entry.Id))
			{
				text2 += "   [restart to apply]";
			}
			return text2;
		}

		internal static void EnsureTab(SettingsTABS tabs)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)tabs == (Object)null)
			{
				return;
			}
			try
			{
				foreach (Transform item in ((Component)tabs).transform)
				{
					if (((Object)item).name == "SpongeUI_ModsTab")
					{
						return;
					}
				}
				SettingsTABSButton val = ((Component)tabs).GetComponentsInChildren<SettingsTABSButton>(true).FirstOrDefault();
				if ((Object)(object)val == (Object)null)
				{
					ModLog log = _log;
					if (log != null)
					{
						log.Warning((object)"No settings tab to clone; the Mods tab was not added.");
					}
					return;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent);
				((Object)val2).name = "SpongeUI_ModsTab";
				val2.transform.SetAsLastSibling();
				LocalizedText[] componentsInChildren = val2.GetComponentsInChildren<LocalizedText>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
				}
				SettingsTABSButton component = val2.GetComponent<SettingsTABSButton>();
				if ((Object)(object)component == (Object)null)
				{
					Object.Destroy((Object)(object)val2);
					return;
				}
				if ((Object)(object)((TAB_Button)component).text != (Object)null)
				{
					((TMP_Text)((TAB_Button)component).text).text = "MODS";
				}
				Button component2 = val2.GetComponent<Button>();
				if ((Object)(object)component2 != (Object)null)
				{
					((UnityEventBase)component2.onClick).RemoveAllListeners();
				}
				ModTab = component;
				if (!((TABS<SettingsTABSButton>)(object)tabs).buttons.Contains(component))
				{
					((TABS<SettingsTABSButton>)(object)tabs).AddButton(component);
				}
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Debug((object)"Mods tab added to the settings screen.");
				}
			}
			catch (Exception ex)
			{
				ModLog log3 = _log;
				if (log3 != null)
				{
					log3.Exception("Failed to add the Mods tab", ex);
				}
			}
		}

		internal static bool IsOurTab(SettingsTABSButton button)
		{
			if ((Object)(object)button != (Object)null && (Object)(object)ModTab != (Object)null)
			{
				return button == ModTab;
			}
			return false;
		}

		internal static void ShowModSettings(SharedSettingsMenu menu)
		{
			if ((Object)(object)menu == (Object)null)
			{
				return;
			}
			try
			{
				SyncSettings();
				Traverse val = Traverse.Create((object)menu);
				List<SettingsUICell> value = val.Field("m_spawnedCells").GetValue<List<SettingsUICell>>();
				GameObject settingsCellPrefab = menu.m_settingsCellPrefab;
				Transform settingsContentParent = menu.m_settingsContentParent;
				if (value == null || (Object)(object)settingsCellPrefab == (Object)null || (Object)(object)settingsContentParent == (Object)null)
				{
					ModLog log = _log;
					if (log != null)
					{
						log.Warning((object)"Settings menu internals not available; cannot show mod settings.");
					}
					return;
				}
				Traverse val2 = val.Field("m_fadeInCoroutine");
				Coroutine value2 = val2.GetValue<Coroutine>();
				if (value2 != null)
				{
					((MonoBehaviour)menu).StopCoroutine(value2);
					val2.SetValue((object)null);
				}
				foreach (SettingsUICell item in value)
				{
					if ((Object)(object)item != (Object)null)
					{
						Object.DestroyImmediate((Object)(object)((Component)item).gameObject);
					}
				}
				value.Clear();
				GameHandler instance = GameHandler.Instance;
				SettingsHandler val3 = ((instance != null) ? instance.SettingsHandler : null);
				if (val3 == null)
				{
					return;
				}
				List<ModToggleSetting> list = (from s in ISettingsHandlerExtensions.GetSettingsThatImplements<IExposedSetting>((ISettingHandler)(object)val3)
					where s.GetCategory() == "Mods"
					select s).OfType<ModToggleSetting>().ToList();
				foreach (ModToggleSetting item2 in list)
				{
					SettingsUICell component = Object.Instantiate<GameObject>(settingsCellPrefab, settingsContentParent).GetComponent<SettingsUICell>();
					if (!((Object)(object)component == (Object)null))
					{
						if (item2.Switch != null && item2.Switch.IsProtected)
						{
							component.ShouldntShow();
						}
						value.Add(component);
						component.Setup<ModToggleSetting>(item2);
					}
				}
				foreach (SettingsUICell item3 in value)
				{
					if ((Object)(object)item3 != (Object)null)
					{
						item3.FadeIn();
					}
				}
				EnsureScrollable(settingsContentParent);
				ModLog log2 = _log;
				if (log2 != null)
				{
					log2.Debug((object)$"Showed {list.Count} mod setting(s).");
				}
			}
			catch (Exception ex)
			{
				ModLog log3 = _log;
				if (log3 != null)
				{
					log3.Exception("Failed to show mod settings", ex);
				}
			}
		}

		private static void EnsureScrollable(Transform content)
		{
			ScrollableList.Refresh(ScrollableList.Ensure(content, _log));
		}
	}
	internal static class ModSettingsPatches
	{
		[HarmonyPatch(typeof(SharedSettingsMenu), "OnEnable")]
		[HarmonyPrefix]
		private static void MenuOpening(SharedSettingsMenu __instance)
		{
			try
			{
				ModSettingsTab.EnsureTab(Traverse.Create((object)__instance).Field("m_tabs").GetValue<SettingsTABS>());
			}
			catch (Exception)
			{
			}
		}

		[HarmonyPatch(typeof(SettingsTABS), "OnSelected")]
		[HarmonyPrefix]
		private static bool TabSelected(SettingsTABS __instance, SettingsTABSButton button)
		{
			if (!ModSettingsTab.IsOurTab(button))
			{
				return true;
			}
			try
			{
				ModSettingsTab.ShowModSettings(__instance.SettingsMenu);
			}
			catch (Exception)
			{
			}
			return false;
		}
	}
	public sealed class ModToggleSetting : BoolSetting, IExposedSetting
	{
		public const string CategoryName = "Mods";

		private readonly ModSwitch _switch;

		private readonly ModLog _log;

		private readonly string _displayName;

		public ModSwitch Switch => _switch;

		public override LocalizedString OffString => null;

		public override LocalizedString OnString => null;

		public ModToggleSetting(ModSwitch modSwitch, string displayName, ModLog log)
		{
			_switch = modSwitch;
			_displayName = displayName;
			_log = log;
			((BoolSetting)this).Value = modSwitch.Value;
		}

		public override void ApplyValue()
		{
			if (_switch == null)
			{
				return;
			}
			if (_switch.IsProtected)
			{
				((BoolSetting)this).Value = _switch.Value;
			}
			else
			{
				if (_switch.Value == ((BoolSetting)this).Value)
				{
					return;
				}
				if (!_switch.TrySet(((BoolSetting)this).Value, _log))
				{
					((BoolSetting)this).Value = _switch.Value;
					return;
				}
				ModRestartTracker.MarkChanged(_switch.Id);
				ModLog log = _log;
				if (log != null)
				{
					log.Info((object)("Mod setting '" + _switch.Id + "' set to " + (((BoolSetting)this).Value ? "on" : "off") + ". Saved; restart the game for it to take effect."));
				}
			}
		}

		public override void Load(ISettingsSaveLoad loader)
		{
			((BoolSetting)this).Value = _switch?.Value ?? false;
		}

		public override void Save(ISettingsSaveLoad saver)
		{
		}

		protected override bool GetDefaultValue()
		{
			return _switch?.Value ?? false;
		}

		public string GetDisplayName()
		{
			return _displayName;
		}

		public string GetCategory()
		{
			return "Mods";
		}
	}
}
namespace SpongePEAK.Lib.Items
{
	public sealed class CookAwareEffects : MonoBehaviour
	{
		internal Dictionary<int, List<StatusEffect>> EffectsByCookCount;

		private Item _item;

		private void Awake()
		{
			_item = ((Component)this).GetComponent<Item>();
		}

		public void ApplyTo(Character character)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)character == (Object)null || character.refs == null || EffectsByCookCount == null)
			{
				return;
			}
			CharacterAfflictions afflictions = character.refs.afflictions;
			if ((Object)(object)afflictions == (Object)null)
			{
				return;
			}
			List<StatusEffect> list = ResolveEffects(GetCookCount());
			if (list == null)
			{
				return;
			}
			foreach (StatusEffect item in list)
			{
				if (item.Amount >= 0f)
				{
					afflictions.AddStatus(item.Status, item.Amount, false, true, true, false);
				}
				else
				{
					afflictions.SubtractStatus(item.Status, Mathf.Abs(item.Amount), false, false);
				}
			}
		}

		public int GetCookCount()
		{
			if ((Object)(object)_item == (Object)null)
			{
				return 0;
			}
			try
			{
				if (!_item.HasData((DataEntryKey)1))
				{
					return 0;
				}
				return _item.GetData<IntItemData>((DataEntryKey)1)?.Value ?? 0;
			}
			catch
			{
				return 0;
			}
		}

		internal List<StatusEffect> ResolveEffects(int cookCount)
		{
			if (EffectsByCookCount == null || EffectsByCookCount.Count == 0)
			{
				return null;
			}
			if (EffectsByCookCount.TryGetValue(cookCount, out var value))
			{
				return value;
			}
			List<StatusEffect> result = null;
			int num = -1;
			foreach (KeyValuePair<int, List<StatusEffect>> item in EffectsByCookCount)
			{
				if (item.Key <= cookCount && item.Key > num)
				{
					num = item.Key;
					result = item.Value;
				}
			}
			return result;
		}
	}
	public sealed class DynamiteFuse : MonoBehaviour
	{
		[SerializeField]
		private float _fuseSeconds = 30f;

		[SerializeField]
		private bool _manualLight = true;

		private Item _item;

		private Dynamite _dynamite;

		private Action _onUsed;

		public bool IsLit
		{
			get
			{
				BoolItemData val = default(BoolItemData);
				if ((Object)(object)_item != (Object)null && _item.data != null && _item.data.TryGetDataEntry<BoolItemData>((DataEntryKey)3, ref val))
				{
					return val.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			_item = ((Component)this).GetComponent<Item>();
			_dynamite = ((Component)this).GetComponent<Dynamite>();
			if (_manualLight && (Object)(object)_item != (Object)null)
			{
				_onUsed = Light;
				_item.OnPrimaryFinishedCast = (Action)Delegate.Combine(_item.OnPrimaryFinishedCast, _onUsed);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_item != (Object)null && _onUsed != null)
			{
				_item.OnPrimaryFinishedCast = (Action)Delegate.Remove(_item.OnPrimaryFinishedCast, _onUsed);
			}
		}

		public void Light()
		{
			if (!((Object)(object)_dynamite == (Object)null) && !IsLit)
			{
				_dynamite.LightFlare();
			}
		}

		internal static bool Apply(GameObject clone, float fuseSeconds, bool manualLight, ModLog log)
		{
			Dynamite component = clone.GetComponent<Dynamite>();
			if ((Object)(object)component == (Object)null)
			{
				if (log != null)
				{
					log.Warning((object)"Cannot apply fuse settings: clone has no Dynamite component.");
				}
				return false;
			}
			component.startingFuseTime = fuseSeconds;
			if (manualLight)
			{
				component.lightFuseRadius = 0f;
				Item component2 = clone.GetComponent<Item>();
				if (component2 != null && component2.UIData != null)
				{
					component2.UIData.hasMainInteract = true;
					ItemLocalization.RegisterRaw("SPONGE_INTERACT_LIGHTFUSE", "Light fuse", log);
					component2.UIData.mainInteractPrompt = "SPONGE_INTERACT_LIGHTFUSE";
				}
			}
			DynamiteFuse dynamiteFuse = clone.AddComponent<DynamiteFuse>();
			dynamiteFuse._fuseSeconds = fuseSeconds;
			dynamiteFuse._manualLight = manualLight;
			if (log != null)
			{
				log.Debug((object)string.Format("Fuse set to {0:F0}s ({1} light).", fuseSeconds, manualLight ? "manual" : "proximity"));
			}
			return true;
		}
	}
	public sealed class ExplodeOnConsume : MonoBehaviourPun
	{
		[SerializeField]
		private GameObject _explosionPrefab;

		private bool _exploded;

		private Item _item;

		private Action _onConsumed;

		internal void Configure(GameObject explosionPrefab)
		{
			_explosionPrefab = explosionPrefab;
		}

		private void Awake()
		{
			_item = ((Component)this).GetComponent<Item>();
			if (!((Object)(object)_item == (Object)null))
			{
				_onConsumed = OnConsumed;
				_item.OnConsumed = (Action)Delegate.Combine(_item.OnConsumed, _onConsumed);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_item != (Object)null && _onConsumed != null)
			{
				_item.OnConsumed = (Action)Delegate.Remove(_item.OnConsumed, _onConsumed);
			}
		}

		private void OnConsumed()
		{
			if (!_exploded && !((Object)(object)((MonoBehaviourPun)this).photonView == (Object)null) && ((MonoBehaviourPun)this).photonView.IsMine)
			{
				_exploded = true;
				((MonoBehaviourPun)this).photonView.RPC("RPC_ConsumeExplode", (RpcTarget)0, Array.Empty<object>());
			}
		}

		[PunRPC]
		private void RPC_ConsumeExplode()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_explosionPrefab == (Object)null))
			{
				Object.Instantiate<GameObject>(_explosionPrefab, ((Component)this).transform.position, ((Component)this).transform.rotation);
			}
		}

		internal static bool Attach(GameObject clone, string explosionSourceItem, ModLog log)
		{
			GameObject val = FindExplosionPrefab(explosionSourceItem, log);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			clone.AddComponent<ExplodeOnConsume>().Configure(val);
			if (log != null)
			{
				log.Debug((object)("Attached consume-explosion sourced from '" + explosionSourceItem + "'."));
			}
			return true;
		}

		private static GameObject FindExplosionPrefab(string itemName, ModLog log)
		{
			Item val = ItemCloner.FindSourceItem(itemName);
			if ((Object)(object)val == (Object)null)
			{
				if (log != null)
				{
					log.Warning((object)("Cannot attach explosion: item '" + itemName + "' not found."));
				}
				return null;
			}
			Dynamite componentInChildren = ((Component)val).GetComponentInChildren<Dynamite>(true);
			if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren.explosionPrefab == (Object)null)
			{
				if (log != null)
				{
					log.Warning((object)("Item '" + itemName + "' has no Dynamite component with an explosion prefab."));
				}
				return null;
			}
			return componentInChildren.explosionPrefab;
		}
	}
	internal static class ItemAppearance
	{
		private static readonly int TintProperty = Shader.PropertyToID("_Tint");

		private static readonly int ColorProperty = Shader.PropertyToID("_Color");

		private static readonly int BaseColorProperty = Shader.PropertyToID("_BaseColor");

		public static void Apply(GameObject clone, ItemCloneDefinition definition, ModLog log)
		{
			Renderer[] componentsInChildren = clone.GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Material[] materials = val.materials;
				foreach (Material val2 in materials)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						if (definition.HueShift.HasValue)
						{
							ShiftHue(val2, definition);
						}
						if (definition.Variant != SpecialVariant.None)
						{
							ApplyVariant(val2, definition.Variant);
						}
					}
				}
				val.materials = materials;
			}
			if (log != null)
			{
				log.Debug((object)("Applied appearance to '" + definition.DisplayName + "'."));
			}
		}

		private static void ShiftHue(Material material, ItemCloneDefinition definition)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			int num = ResolveColorProperty(material);
			if (num != 0)
			{
				Color color = material.GetColor(num);
				float num2 = default(float);
				float num3 = default(float);
				float num4 = default(float);
				Color.RGBToHSV(color, ref num2, ref num3, ref num4);
				num2 = Mathf.Repeat(num2 + definition.HueShift.Value / 360f, 1f);
				num3 = Mathf.Clamp01(num3 * definition.SaturationScale);
				num4 = Mathf.Clamp01(num4 * definition.BrightnessScale);
				Color val = Color.HSVToRGB(num2, num3, num4);
				val.a = color.a;
				material.SetColor(num, val);
			}
		}

		private static int ResolveColorProperty(Material material)
		{
			if (material.HasProperty(TintProperty))
			{
				return TintProperty;
			}
			if (material.HasProperty(BaseColorProperty))
			{
				return BaseColorProperty;
			}
			if (material.HasProperty(ColorProperty))
			{
				return ColorProperty;
			}
			return 0;
		}

		private static void ApplyVariant(Material material, SpecialVariant variant)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			int num = ResolveColorProperty(material);
			switch (variant)
			{
			case SpecialVariant.Golden:
				if (num != 0)
				{
					material.SetColor(num, new Color(1f, 0.84f, 0.25f, material.GetColor(num).a));
				}
				TrySetFloat(material, "_Metallic", 1f);
				TrySetFloat(material, "_Glossiness", 0.85f);
				TrySetFloat(material, "_Smoothness", 0.85f);
				break;
			case SpecialVariant.Spectral:
				if (num != 0)
				{
					Color val = default(Color);
					((Color)(ref val))..ctor(0.6f, 0.85f, 1f, 0.45f);
					material.SetColor(num, val);
				}
				TrySetFloat(material, "_Metallic", 0f);
				TrySetFloat(material, "_Glossiness", 0.2f);
				break;
			case SpecialVariant.Antigrav:
				if (num != 0)
				{
					material.SetColor(num, new Color(0.7f, 0.4f, 1f, material.GetColor(num).a));
				}
				TrySetFloat(material, "_Glossiness", 0.6f);
				break;
			}
		}

		private static void TrySetFloat(Material material, string property, float value)
		{
			if (material.HasProperty(property))
			{
				material.SetFloat(property, value);
			}
		}
	}
	public sealed class ItemBehaviourSpec
	{
		internal List<Type> ComponentsToRemove { get; } = new List<Type>();

		internal List<(Type Type, Action<Component> Configure)> ComponentsToAdd { get; } = new List<(Type, Action<Component>)>();

		internal bool StripAllActions { get; private set; }

		internal bool StripAllComponents { get; private set; }

		internal Action<Item> ConfigureUI { get; private set; }

		public ItemBehaviourSpec ClearActions()
		{
			StripAllActions = true;
			return this;
		}

		public ItemBehaviourSpec ClearComponents()
		{
			StripAllComponents = true;
			return this;
		}

		public ItemBehaviourSpec Remove<T>() where T : Component
		{
			ComponentsToRemove.Add(typeof(T));
			return this;
		}

		public ItemBehaviourSpec Add<T>(Action<T> configure = null) where T : Component
		{
			ComponentsToAdd.Add((typeof(T), (configure == null) ? null : ((Action<Component>)delegate(Component c)
			{
				configure((T)(object)c);
			})));
			return this;
		}

		public ItemBehaviourSpec ConfigureUIData(Action<Item> configure)
		{
			ConfigureUI = configure;
			return this;
		}

		internal void Apply(GameObject clone, Item item, ModLog log)
		{
			if (StripAllActions)
			{
				ItemActionBase[] componentsInChildren = clone.GetComponentsInChildren<ItemActionBase>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
				}
			}
			if (StripAllComponents)
			{
				ItemComponent[] componentsInChildren2 = clone.GetComponentsInChildren<ItemComponent>(true);
				for (int i = 0; i < componentsInChildren2.Length; i++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
				}
			}
			foreach (Type item2 in ComponentsToRemove)
			{
				Component[] componentsInChildren3 = clone.GetComponentsInChildren(item2, true);
				for (int i = 0; i < componentsInChildren3.Length; i++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren3[i]);
				}
			}
			foreach (var (type, action) in ComponentsToAdd)
			{
				try
				{
					Component obj = clone.AddComponent(type);
					action?.Invoke(obj);
				}
				catch (Exception ex)
				{
					if (log != null)
					{
						log.Exception("Failed to add component '" + type.Name + "' to '" + ((Object)clone).name + "'", ex);
					}
				}
			}
			try
			{
				ConfigureUI?.Invoke(item);
			}
			catch (Exception ex2)
			{
				if (log != null)
				{
					log.Exception("Failed to configure UI data for '" + ((Object)clone).name + "'", ex2);
				}
			}
		}
	}
	public static class ItemCatalog
	{
		public static List<(string PrefabName, string DisplayName, ushort Id)> All()
		{
			List<(string, string, ushort)> list = new List<(string, string, ushort)>();
			ItemDatabase instance = SingletonAsset<ItemDatabase>.Instance;
			if (instance?.itemLookup == null)
			{
				return list;
			}
			foreach (KeyValuePair<ushort, Item> item in instance.itemLookup)
			{
				Item value = item.Value;
				if (!((Object)(object)value == (Object)null))
				{
					list.Add((((Object)value).name, value.UIData?.itemName ?? "<none>", item.Key));
				}
			}
			return list;
		}

		public static void Dump(ModLog log, string filter = null)
		{
			if (log == null)
			{
				return;
			}
			List<(string, string, ushort)> list = All();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine($"Item catalog ({list.Count} entries):");
			foreach (var (text, text2, num) in list)
			{
				if (string.IsNullOrEmpty(filter) || text.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					stringBuilder.AppendLine($"  [{num}] prefab='{text}' name='{text2}'");
				}
			}
			log.Info((object)stringBuilder.ToString());
		}

		public static void DumpMaterials(ModLog log, string itemName)
		{
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			if (log == null || string.IsNullOrEmpty(itemName))
			{
				return;
			}
			ItemDatabase instance = SingletonAsset<ItemDatabase>.Instance;
			if (instance?.itemLookup == null)
			{
				log.Warning((object)"Item database is not loaded yet.");
				return;
			}
			foreach (KeyValuePair<ushort, Item> item in instance.itemLookup)
			{
				Item value = item.Value;
				if ((Object)(object)value == (Object)null || (!string.Equals(((Object)value).name, itemName, StringComparison.OrdinalIgnoreCase) && (value.UIData == null || !string.Equals(value.UIData.itemName, itemName, StringComparison.OrdinalIgnoreCase))))
				{
					continue;
				}
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine($"Materials for '{itemName}' (id {item.Key}):");
				Renderer[] componentsInChildren = ((Component)value).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Material[] sharedMaterials = val.sharedMaterials;
					foreach (Material val2 in sharedMaterials)
					{
						if ((Object)(object)val2 == (Object)null)
						{
							continue;
						}
						string[] obj = new string[7]
						{
							"  renderer='",
							((Object)val).name,
							"' material='",
							((Object)val2).name,
							"' shader='",
							null,
							null
						};
						Shader shader = val2.shader;
						obj[5] = ((shader != null) ? ((Object)shader).name : null);
						obj[6] = "'";
						stringBuilder.AppendLine(string.Concat(obj));
						string[] array = new string[3] { "_Tint", "_Color", "_BaseColor" };
						foreach (string text in array)
						{
							if (val2.HasProperty(text))
							{
								stringBuilder.AppendLine($"      {text} = {val2.GetColor(text)}");
							}
						}
					}
				}
				log.Info((object)stringBuilder.ToString());
				return;
			}
			log.Warning((object)("No item named '" + itemName + "' found."));
		}

		public static void DumpBehaviour(ModLog log, string itemName)
		{
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			if (log == null || string.IsNullOrEmpty(itemName))
			{
				return;
			}
			ItemDatabase instance = SingletonAsset<ItemDatabase>.Instance;
			if (instance?.itemLookup == null)
			{
				return;
			}
			foreach (KeyValuePair<ushort, Item> item in instance.itemLookup)
			{
				Item value = item.Value;
				if (!((Object)(object)value == (Object)null) && (string.Equals(((Object)value).name, itemName, StringComparison.OrdinalIgnoreCase) || (value.UIData != null && string.Equals(value.UIData.itemName, itemName, StringComparison.OrdinalIgnoreCase))))
				{
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.AppendLine($"Behaviour for '{itemName}' (id {item.Key}):");
					stringBuilder.AppendLine("  Actions:");
					ItemActionBase[] componentsInChildren = ((Component)value).GetComponentsInChildren<ItemActionBase>(true);
					foreach (ItemActionBase val in componentsInChildren)
					{
						stringBuilder.AppendLine("    " + ((object)val).GetType().Name);
					}
					stringBuilder.AppendLine("  Components:");
					ItemComponent[] componentsInChildren2 = ((Component)value).GetComponentsInChildren<ItemComponent>(true);
					foreach (ItemComponent val2 in componentsInChildren2)
					{
						stringBuilder.AppendLine("    " + ((object)val2).GetType().Name);
					}
					LootData component = ((Component)value).GetComponent<LootData>();
					if ((Object)(object)component != (Object)null)
					{
						stringBuilder.AppendLine($"  Spawns: {component.spawnLocations} at {component.Rarity}");
					}
					log.Info((object)stringBuilder.ToString());
					break;
				}
			}
		}
	}
	public sealed class ItemCloneDefinition
	{
		public const float MaxScale = 5f;

		public string SourceItemName { get; }

		public List<string> AlternateSourceNames { get; } = new List<string>();

		public string InternalName { get; }

		public string DisplayName { get; private set; }

		public string Description { get; private set; }

		public float? HueShift { get; private set; }

		public float SaturationScale { get; private set; } = 1f;

		public float BrightnessScale { get; private set; } = 1f;

		public float Scale { get; private set; } = 1f;

		public SpecialVariant Variant { get; private set; }

		public Dictionary<int, List<StatusEffect>> EffectsByCookCount { get; } = new Dictionary<int, List<StatusEffect>>();

		public ExplosionOverride Explosion { get; private set; }

		public FuseOverride Fuse { get; private set; }

		public string ExplodeOnConsumeSource { get; private set; }

		public SpawnRule Spawning { get; private set; }

		public ItemBehaviourSpec Behaviour { get; private set; }

		public Action<GameObject> PostBuild { get; private set; }

		public ItemCloneDefinition(string sourceItemName, string internalName, string displayName = null)
		{
			if (string.IsNullOrWhiteSpace(sourceItemName))
			{
				throw new ArgumentException("Source item name is required.", "sourceItemName");
			}
			if (string.IsNullOrWhiteSpace(internalName))
			{
				throw new ArgumentException("Internal name is required