Decompiled source of NotYourLoot v0.7.1

NotYourLoot.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EpicLoot;
using EpicLoot_UnityLib;
using HarmonyLib;
using Jotunn.Managers;
using TMPro;
using UnityEngine;
using UnityEngine.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("NotYourLoot")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("NotYourLoot")]
[assembly: AssemblyTitle("NotYourLoot")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace NotYourLoot;

internal static class AdventureBackpackTooltip
{
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	private static class GetTooltipPatch
	{
		private static void Postfix(ItemData item, bool appending, ref string __result)
		{
			if (!appending && item != null && !string.IsNullOrEmpty(__result) && !__result.Contains("Items inside weigh ") && TryGetMultiplier(item, out var multiplier))
			{
				int num = Mathf.RoundToInt((1f - multiplier) * 100f);
				if (num > 0)
				{
					__result = __result + "\nItems inside weigh " + num + "% less";
				}
			}
		}
	}

	private const string AssemblyName = "AdventureBackpacks";

	private const string Prefix = "Items inside weigh ";

	private static MethodInfo _tryGetBackpackItem;

	private static bool TryGetMultiplier(ItemData item, out float multiplier)
	{
		multiplier = 1f;
		try
		{
			if (item == null)
			{
				return false;
			}
			if (_tryGetBackpackItem == null && !ResolveBackpackMethod())
			{
				return false;
			}
			object[] array = new object[2] { item, null };
			object obj = _tryGetBackpackItem.Invoke(null, array);
			if (!(obj is bool) || !(bool)obj || array[1] == null)
			{
				return false;
			}
			object obj2 = array[1];
			FieldInfo fieldInfo = AccessTools.Field(obj2.GetType(), "WeightMultiplier");
			if (fieldInfo == null)
			{
				return false;
			}
			object value = fieldInfo.GetValue(obj2);
			if (value == null)
			{
				return false;
			}
			PropertyInfo property = value.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
			if (property == null)
			{
				return false;
			}
			if (!(property.GetValue(value) is float num))
			{
				return false;
			}
			multiplier = Mathf.Clamp01(num);
			return true;
		}
		catch
		{
			return false;
		}
	}

	private static bool ResolveBackpackMethod()
	{
		Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "AdventureBackpacks");
		if (assembly == null)
		{
			return false;
		}
		Type[] array;
		try
		{
			array = assembly.GetTypes();
		}
		catch (ReflectionTypeLoadException ex)
		{
			array = ex.Types.Where((Type t) => t != null).ToArray();
		}
		Type[] array2 = array;
		for (int num = 0; num < array2.Length; num++)
		{
			MethodInfo methodInfo = array2[num].GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "TryGetBackpackItem")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 2 && parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType.IsByRef && m.ReturnType == typeof(bool);
			});
			if (methodInfo != null)
			{
				_tryGetBackpackItem = methodInfo;
				return true;
			}
		}
		return false;
	}
}
internal static class AudioPatches
{
	private static readonly HashSet<int> CaptainScaledObjects = new HashSet<int>();

	private static readonly HashSet<int> EpicMmoScaledObjects = new HashSet<int>();

	private static readonly HashSet<string> EpicMmoLoggedPrefabs = new HashSet<string>(StringComparer.Ordinal);

	private static bool _captainInstalled;

	private static AudioClip _heroAwakensReplacementClip;

	private static AudioSource _heroAwakensReplacementSource;

	private static AudioClip _questCompleteReplacementClip;

	private static AudioSource _questCompleteReplacementSource;

	internal static void Install(Harmony harmony)
	{
		if (harmony != null)
		{
			InstallCaptainPatches(harmony);
			InstallEpicMmoPatches(harmony);
		}
	}

	internal static IEnumerator DelayedEpicMmoScan()
	{
		int attempt = 0;
		while (attempt < 10)
		{
			if (attempt > 0)
			{
				yield return (object)new WaitForSeconds(1f);
			}
			int num = ScaleEpicMmoAssets();
			if (num > 0)
			{
				NotYourLootPlugin.LogInfo($"EpicMMO audio normalization applied to {num} AudioSource(s).");
			}
			int num2 = attempt + 1;
			attempt = num2;
		}
	}

	internal static IEnumerator DelayedEpicMmoOrbEffectCleanup()
	{
		int attempt = 0;
		while (attempt < 10)
		{
			if (attempt > 0)
			{
				yield return (object)new WaitForSeconds(1f);
			}
			if (!((Object)(object)ObjectDB.instance == (Object)null))
			{
				bool flag = false;
				int num = 0;
				for (int i = 1; i <= 8; i++)
				{
					string text = $"mmo_orb{i}";
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
					if ((Object)(object)itemPrefab == (Object)null)
					{
						continue;
					}
					ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
					if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null)
					{
						continue;
					}
					flag = true;
					StatusEffect consumeStatusEffect = component.m_itemData.m_shared.m_consumeStatusEffect;
					if ((Object)(object)consumeStatusEffect == (Object)null || consumeStatusEffect.m_startEffects == null || consumeStatusEffect.m_startEffects.m_effectPrefabs == null)
					{
						continue;
					}
					EffectData[] effectPrefabs = consumeStatusEffect.m_startEffects.m_effectPrefabs;
					foreach (EffectData val in effectPrefabs)
					{
						if (val != null && !((Object)(object)val.m_prefab == (Object)null) && val.m_enabled && (string.Equals(((Object)val.m_prefab).name, "fx_GP_Activation", StringComparison.Ordinal) || string.Equals(((Object)val.m_prefab).name, "sfx_Potion_stamina_Start", StringComparison.Ordinal)))
						{
							val.m_enabled = false;
							num++;
						}
					}
				}
				if (flag)
				{
					NotYourLootPlugin.LogInfo("EpicMMO XP orb cleanup: disabled " + $"{num} unwanted start effect(s).");
					yield break;
				}
			}
			int j = attempt + 1;
			attempt = j;
		}
		NotYourLootPlugin.LogWarning("EpicMMO XP orb cleanup could not find registered XP orb prefabs.");
	}

	internal static int ScaleEpicMmoAssets()
	{
		if (NotYourLootPlugin.AudioEpicMmoEnabled == null || !NotYourLootPlugin.AudioEpicMmoEnabled.Value)
		{
			return 0;
		}
		float value = NotYourLootPlugin.AudioEpicMmoVolume.Value;
		int num = 0;
		bool flag = false;
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			if ((assembly.GetName().Name ?? string.Empty).IndexOf("EpicMMO", StringComparison.OrdinalIgnoreCase) < 0)
			{
				continue;
			}
			flag = true;
			foreach (Type loadableType in GetLoadableTypes(assembly))
			{
				FieldInfo[] fields;
				try
				{
					fields = loadableType.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				catch
				{
					continue;
				}
				FieldInfo[] array = fields;
				foreach (FieldInfo fieldInfo in array)
				{
					if (string.Equals(fieldInfo.FieldType.FullName, "UnityEngine.AssetBundle", StringComparison.Ordinal))
					{
						object value2;
						try
						{
							value2 = fieldInfo.GetValue(null);
						}
						catch
						{
							continue;
						}
						if (value2 != null)
						{
							num += ScaleGameObjectsFromAssetBundle(value2, fieldInfo.FieldType, value);
						}
					}
				}
			}
		}
		if ((Object)(object)ZNetScene.instance != (Object)null)
		{
			num += ScaleEpicPrefabByName("LevelUpVFX", value);
			num += ScaleEpicPrefabByName("LevelUpVFX2", value);
		}
		if (!flag && EpicMmoScaledObjects.Count == 0)
		{
			return 0;
		}
		return num;
	}

	private static int ScaleGameObjectsFromAssetBundle(object bundle, Type assetBundleType, float multiplier)
	{
		try
		{
			MethodInfo method = assetBundleType.GetMethod("LoadAllAssets", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(Type) }, null);
			if (method == null)
			{
				return 0;
			}
			if (!(method.Invoke(bundle, new object[1] { typeof(GameObject) }) is Array array))
			{
				return 0;
			}
			int num = 0;
			foreach (object item in array)
			{
				GameObject val = (GameObject)((item is GameObject) ? item : null);
				if (val != null)
				{
					num += ScaleAudioSources(val, multiplier, EpicMmoScaledObjects);
				}
			}
			return num;
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("Could not inspect an EpicMMO asset bundle for audio: " + ex.Message);
			return 0;
		}
	}

	private static int ScaleEpicPrefabByName(string prefabName, float multiplier)
	{
		try
		{
			GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
			return ScaleAudioSources(prefab, multiplier, EpicMmoScaledObjects) + AdjustEpicMmoLevelUpEffects(prefabName, prefab);
		}
		catch
		{
			return 0;
		}
	}

	private static int AdjustEpicMmoLevelUpEffects(string prefabName, GameObject prefab)
	{
		if ((Object)(object)prefab == (Object)null)
		{
			return 0;
		}
		int num = 0;
		Transform val = prefab.transform.Find("Wings buff cast");
		if ((Object)(object)val != (Object)null)
		{
			((Component)val).gameObject.SetActive(false);
			num++;
			NotYourLootPlugin.LogInfo("Disabled EpicMMO Wings buff effect on " + prefabName + ".");
		}
		if (prefabName == "LevelUpVFX2")
		{
			AudioSource[] componentsInChildren = prefab.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.clip == (Object)null) && !(((Object)val2.clip).name != "level_up_wav"))
				{
					val2.volume = 0f;
					val2.playOnAwake = false;
					val2.mute = true;
					num++;
					NotYourLootPlugin.LogInfo("Suppressed EpicMMO custom level-up theme: level_up_wav");
				}
			}
		}
		return num;
	}

	private static string GetTransformPath(Transform current, Transform root)
	{
		if ((Object)(object)current == (Object)null)
		{
			return "<null>";
		}
		List<string> list = new List<string>();
		while ((Object)(object)current != (Object)null)
		{
			list.Add(((Object)current).name);
			if ((Object)(object)current == (Object)(object)root)
			{
				break;
			}
			current = current.parent;
		}
		list.Reverse();
		return string.Join("/", list);
	}

	private static void InstallEpicMmoPatches(Harmony harmony)
	{
		//IL_0084: 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_0097: Expected O, but got Unknown
		//IL_0097: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("EpicMMOSystem.PlayerFVX");
		if (type == null)
		{
			NotYourLootPlugin.LogInfo("EpicMMO PlayerFVX not detected; runtime level-up patch skipped.");
			return;
		}
		MethodInfo methodInfo = AccessTools.Method(type, "levelUp", (Type[])null, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(AudioPatches), "EpicMmoLevelUpPrefix", (Type[])null, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(typeof(AudioPatches), "EpicMmoLevelUpPostfix", (Type[])null, (Type[])null);
		if (methodInfo == null || methodInfo2 == null || methodInfo3 == null)
		{
			NotYourLootPlugin.LogWarning("Could not install EpicMMO level-up runtime patch.");
			return;
		}
		harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		NotYourLootPlugin.LogInfo("EpicMMO level-up prefix/postfix patches installed.");
	}

	private static void EpicMmoLevelUpPrefix()
	{
		try
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			string[] array = new string[2] { "LevelUpVFX", "LevelUpVFX2" };
			foreach (string text in array)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(text);
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Transform[] componentsInChildren = prefab.GetComponentsInChildren<Transform>(true);
				foreach (Transform val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					if (((Object)val).name == "Wings buff cast")
					{
						AudioSource[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<AudioSource>(true);
						foreach (AudioSource val2 in componentsInChildren2)
						{
							if (!((Object)(object)val2 == (Object)null))
							{
								val2.Stop();
								val2.volume = 0f;
								val2.playOnAwake = false;
								val2.mute = true;
								num2++;
							}
						}
						((Component)val).gameObject.SetActive(false);
						num++;
					}
					else if (((Object)val).name == "WindsSpinePivot" || ((Object)val).name == "Wings buff")
					{
						((Component)val).gameObject.SetActive(false);
						num++;
					}
					else
					{
						AudioSource component = ((Component)val).GetComponent<AudioSource>();
						if (!((Object)(object)component == (Object)null) && !((Object)(object)component.clip == (Object)null) && ((Object)component.clip).name == "level_up_wav")
						{
							component.Stop();
							component.volume = 0f;
							component.playOnAwake = false;
							component.mute = true;
							num2++;
						}
					}
				}
			}
			NotYourLootPlugin.LogInfo("EpicMMO pre-spawn level-up cleanup: " + $"{num} Wings object(s) disabled, " + $"{num2} AudioSource(s) muted.");
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("EpicMMO pre-spawn level-up cleanup failed: " + ex.Message);
		}
	}

	private static void EpicMmoLevelUpPostfix()
	{
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			Transform[] componentsInChildren = ((Component)localPlayer).GetComponentsInChildren<Transform>(true);
			int num = 0;
			int num2 = 0;
			Transform[] array = componentsInChildren;
			foreach (Transform val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				if (((Object)val).name == "Wings buff cast" && IsEpicMmoLevelUpChild(val))
				{
					AudioSource[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<AudioSource>(true);
					foreach (AudioSource val2 in componentsInChildren2)
					{
						if (!((Object)(object)val2 == (Object)null))
						{
							val2.Stop();
							val2.volume = 0f;
							val2.playOnAwake = false;
							val2.mute = true;
						}
					}
					((Component)val).gameObject.SetActive(false);
					num++;
				}
				else
				{
					AudioSource component = ((Component)val).GetComponent<AudioSource>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.clip == (Object)null) && ((Object)component.clip).name == "level_up_wav" && IsEpicMmoLevelUpChild(val))
					{
						component.Stop();
						component.volume = 0f;
						component.playOnAwake = false;
						component.mute = true;
						num2++;
					}
				}
			}
			NotYourLootPlugin.LogInfo("EpicMMO live level-up cleanup: " + $"{num} Wings object(s) disabled, " + $"{num2} theme source(s) muted.");
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("EpicMMO live level-up cleanup failed: " + ex.Message);
		}
	}

	private static bool IsEpicMmoLevelUpChild(Transform transform)
	{
		Transform val = transform;
		while ((Object)(object)val != (Object)null)
		{
			if ((((Object)val).name ?? string.Empty).StartsWith("LevelUpVFX", StringComparison.Ordinal))
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private static IEnumerable<Type> GetLoadableTypes(Assembly assembly)
	{
		try
		{
			return assembly.GetTypes();
		}
		catch (ReflectionTypeLoadException ex)
		{
			return ex.Types.Where((Type type) => type != null);
		}
		catch
		{
			return Enumerable.Empty<Type>();
		}
	}

	private static void InstallCaptainPatches(Harmony harmony)
	{
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("CaptainSkillTree.SimpleVFX");
		if (type == null)
		{
			NotYourLootPlugin.LogInfo("CaptainSkillTree not detected; Captain audio patches skipped.");
			return;
		}
		MethodInfo methodInfo = AccessTools.Method(typeof(AudioPatches), "CaptainGameObjectPostfix", (Type[])null, (Type[])null);
		string[] source = new string[7] { "InstantiateVFX", "Play", "PlayAtPosition", "PlayFollowing", "PlayOnPlayer", "PlayOnTarget", "PlayWithRotation" };
		int num = 0;
		MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo methodInfo2 in methods)
		{
			if (!(methodInfo2.ReturnType != typeof(GameObject)) && source.Contains(methodInfo2.Name))
			{
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				num++;
			}
		}
		num += PatchSingleFloatEntryPoint(harmony, "CaptainSkillTree.Audio.QuestCompleteSoundManager", "PlayOnce", "QuestCompleteVolumePrefix");
		num += PatchSingleFloatEntryPoint(harmony, "CaptainSkillTree.Audio.HeroAwakensSoundManager", "PlayOnce", "SuppressCaptainHeroAwakensPrefix");
		num += PatchPoleSwingEntryPoint(harmony);
		_captainInstalled = num > 0;
		NotYourLootPlugin.LogInfo(_captainInstalled ? $"CaptainSkillTree audio normalization installed ({num} hook(s))." : "CaptainSkillTree detected, but no supported audio hooks were found.");
	}

	private static int PatchSingleFloatEntryPoint(Harmony harmony, string typeName, string methodName, string prefixName)
	{
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Expected O, but got Unknown
		Type type = AccessTools.TypeByName(typeName);
		if (type == null)
		{
			return 0;
		}
		MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo candidate)
		{
			if (candidate.Name != methodName)
			{
				return false;
			}
			ParameterInfo[] parameters = candidate.GetParameters();
			return parameters.Length == 1 && parameters[0].ParameterType == typeof(float);
		});
		if (methodInfo == null)
		{
			return 0;
		}
		MethodInfo methodInfo2 = AccessTools.Method(typeof(AudioPatches), prefixName, (Type[])null, (Type[])null);
		harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		return 1;
	}

	private static int PatchPoleSwingEntryPoint(Harmony harmony)
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("CaptainSkillTree.Audio.PoleSwingSoundManager");
		if (type == null)
		{
			return 0;
		}
		MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo candidate)
		{
			if (candidate.Name != "PlaySwing")
			{
				return false;
			}
			ParameterInfo[] parameters = candidate.GetParameters();
			return parameters.Length == 2 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(float);
		});
		if (methodInfo == null)
		{
			return 0;
		}
		MethodInfo methodInfo2 = AccessTools.Method(typeof(AudioPatches), "CaptainVolume1Prefix", (Type[])null, (Type[])null);
		harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		return 1;
	}

	private static void CaptainGameObjectPostfix(GameObject __result)
	{
		if (NotYourLootPlugin.AudioCaptainEnabled != null && NotYourLootPlugin.AudioCaptainEnabled.Value)
		{
			ScaleAudioSources(__result, NotYourLootPlugin.AudioCaptainVolume.Value, CaptainScaledObjects, routeToGuiMixer: true);
		}
	}

	private static bool SuppressCaptainHeroAwakensPrefix(ref float __0)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Expected O, but got Unknown
		try
		{
			AudioClip heroAwakensReplacementClip = GetHeroAwakensReplacementClip();
			if ((Object)(object)heroAwakensReplacementClip != (Object)null)
			{
				if ((Object)(object)_heroAwakensReplacementSource == (Object)null)
				{
					GameObject val = new GameObject("NotYourLoot_HeroAwakensReplacement");
					Object.DontDestroyOnLoad((Object)val);
					_heroAwakensReplacementSource = val.AddComponent<AudioSource>();
					_heroAwakensReplacementSource.playOnAwake = false;
					_heroAwakensReplacementSource.loop = false;
					_heroAwakensReplacementSource.spatialBlend = 0f;
					RouteToValheimGuiMixer(_heroAwakensReplacementSource);
				}
				float num = NotYourLootPlugin.AudioCaptainLevelUpVolume?.Value ?? 0.65f;
				_heroAwakensReplacementSource.PlayOneShot(heroAwakensReplacementClip, num);
				NotYourLootPlugin.LogInfo("CaptainSkillTree HeroAwakens replaced with LockChime.wav " + $"(volume {num:0.###}).");
			}
			else
			{
				NotYourLootPlugin.LogWarning("LockChime.wav could not be loaded; CaptainSkillTree HeroAwakens remains suppressed.");
			}
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("HeroAwakens replacement failed: " + ex.Message);
		}
		return false;
	}

	private static AudioClip GetHeroAwakensReplacementClip()
	{
		if ((Object)(object)_heroAwakensReplacementClip != (Object)null)
		{
			return _heroAwakensReplacementClip;
		}
		_heroAwakensReplacementClip = LoadPcmWav(Path.Combine(Paths.PluginPath, "NotYourFriend-NotYourLoot", "LockChime.wav"), "NotYourLoot_LockChime", "HeroAwakens replacement");
		return _heroAwakensReplacementClip;
	}

	private static AudioClip GetQuestCompleteReplacementClip()
	{
		if ((Object)(object)_questCompleteReplacementClip != (Object)null)
		{
			return _questCompleteReplacementClip;
		}
		_questCompleteReplacementClip = LoadPcmWav(Path.Combine(Paths.PluginPath, "NotYourFriend-NotYourLoot", "QuestComplete.wav"), "NotYourLoot_QuestComplete", "Quest-complete replacement");
		return _questCompleteReplacementClip;
	}

	internal static AudioClip LoadPcmWav(string path, string clipName, string logName)
	{
		if (!File.Exists(path))
		{
			NotYourLootPlugin.LogWarning(logName + " WAV not found: " + path);
			return null;
		}
		using FileStream fileStream = File.OpenRead(path);
		using BinaryReader binaryReader = new BinaryReader(fileStream);
		string text = new string(binaryReader.ReadChars(4));
		binaryReader.ReadInt32();
		string text2 = new string(binaryReader.ReadChars(4));
		if (text != "RIFF" || text2 != "WAVE")
		{
			throw new InvalidDataException(Path.GetFileName(path) + " is not a RIFF/WAVE file.");
		}
		ushort num = 0;
		ushort num2 = 0;
		int num3 = 0;
		ushort num4 = 0;
		byte[] array = null;
		while (fileStream.Position + 8 <= fileStream.Length)
		{
			string text3 = new string(binaryReader.ReadChars(4));
			int num5 = binaryReader.ReadInt32();
			long position = fileStream.Position;
			if (text3 == "fmt ")
			{
				num = binaryReader.ReadUInt16();
				num2 = binaryReader.ReadUInt16();
				num3 = binaryReader.ReadInt32();
				binaryReader.ReadInt32();
				binaryReader.ReadUInt16();
				num4 = binaryReader.ReadUInt16();
			}
			else if (text3 == "data")
			{
				array = binaryReader.ReadBytes(num5);
			}
			long num6 = position + num5;
			if ((num5 & 1) != 0)
			{
				num6++;
			}
			fileStream.Position = Math.Min(num6, fileStream.Length);
		}
		if (num != 1)
		{
			throw new InvalidDataException($"Unsupported WAV format {num}; PCM required.");
		}
		if (num2 == 0 || num3 <= 0)
		{
			throw new InvalidDataException("Invalid WAV channel/sample-rate information.");
		}
		if (num4 != 16)
		{
			throw new InvalidDataException($"Unsupported WAV bit depth {num4}; 16-bit required.");
		}
		if (array == null || array.Length == 0)
		{
			throw new InvalidDataException("WAV contains no PCM data.");
		}
		int num7 = array.Length / 2;
		int num8 = num7 / num2;
		float[] array2 = new float[num7];
		for (int i = 0; i < num7; i++)
		{
			short num9 = (short)(array[i * 2] | (array[i * 2 + 1] << 8));
			array2[i] = (float)num9 / 32768f;
		}
		AudioClip val = AudioClip.Create(clipName, num8, (int)num2, num3, false);
		val.SetData(array2, 0);
		NotYourLootPlugin.LogInfo("Loaded " + logName + ": " + Path.GetFileName(path) + " " + $"({num2}ch, {num3} Hz, {num4}-bit).");
		return val;
	}

	private static void CaptainVolume0Prefix(ref float __0)
	{
		if (NotYourLootPlugin.AudioCaptainEnabled != null && NotYourLootPlugin.AudioCaptainEnabled.Value)
		{
			__0 = Mathf.Max(0f, __0 * NotYourLootPlugin.AudioCaptainVolume.Value);
		}
	}

	private static void CaptainVolume1Prefix(ref float __1)
	{
		if (NotYourLootPlugin.AudioCaptainEnabled != null && NotYourLootPlugin.AudioCaptainEnabled.Value)
		{
			__1 = Mathf.Max(0f, __1 * NotYourLootPlugin.AudioCaptainVolume.Value);
		}
	}

	private static bool QuestCompleteVolumePrefix(ref float __0)
	{
		//IL_003a: 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_0045: Expected O, but got Unknown
		if (NotYourLootPlugin.AudioCaptainQuestEnabled == null || !NotYourLootPlugin.AudioCaptainQuestEnabled.Value)
		{
			return true;
		}
		try
		{
			AudioClip questCompleteReplacementClip = GetQuestCompleteReplacementClip();
			if ((Object)(object)questCompleteReplacementClip != (Object)null)
			{
				if ((Object)(object)_questCompleteReplacementSource == (Object)null)
				{
					GameObject val = new GameObject("NotYourLoot_QuestCompleteReplacement");
					Object.DontDestroyOnLoad((Object)val);
					_questCompleteReplacementSource = val.AddComponent<AudioSource>();
					_questCompleteReplacementSource.playOnAwake = false;
					_questCompleteReplacementSource.loop = false;
					_questCompleteReplacementSource.spatialBlend = 0f;
					RouteToValheimGuiMixer(_questCompleteReplacementSource);
				}
				float num = Mathf.Clamp01(NotYourLootPlugin.AudioCaptainQuestVolume.Value);
				_questCompleteReplacementSource.PlayOneShot(questCompleteReplacementClip, num);
				NotYourLootPlugin.LogInfo("CaptainSkillTree quest-complete sound replaced with QuestComplete.wav " + $"(volume {num:0.###}).");
			}
			else
			{
				NotYourLootPlugin.LogWarning("QuestComplete.wav could not be loaded; CaptainSkillTree quest-complete audio remains suppressed.");
			}
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("Quest-complete replacement failed: " + ex.Message);
		}
		return false;
	}

	internal static void RouteToValheimGuiMixer(AudioSource source)
	{
		if (!((Object)(object)source == (Object)null) && !((Object)(object)AudioMan.instance == (Object)null) && !((Object)(object)AudioMan.instance.m_guiMixer == (Object)null))
		{
			source.outputAudioMixerGroup = AudioMan.instance.m_guiMixer;
		}
	}

	private static int ScaleAudioSources(GameObject gameObject, float multiplier, HashSet<int> scaledObjects, bool routeToGuiMixer = false)
	{
		if ((Object)(object)gameObject == (Object)null)
		{
			return 0;
		}
		int instanceID = ((Object)gameObject).GetInstanceID();
		if (!scaledObjects.Add(instanceID))
		{
			return 0;
		}
		AudioSource[] componentsInChildren = gameObject.GetComponentsInChildren<AudioSource>(true);
		if (componentsInChildren == null || componentsInChildren.Length == 0)
		{
			return 0;
		}
		int num = 0;
		AudioSource[] array = componentsInChildren;
		foreach (AudioSource val in array)
		{
			if (!((Object)(object)val == (Object)null))
			{
				val.volume = Mathf.Clamp01(val.volume * multiplier);
				if (routeToGuiMixer)
				{
					RouteToValheimGuiMixer(val);
				}
				num++;
			}
		}
		return num;
	}
}
[HarmonyPatch(typeof(ZNetScene), "Awake")]
internal static class EpicMmoAudioScenePatch
{
	private static void Postfix()
	{
		if ((Object)(object)NotYourLootPlugin.Instance != (Object)null)
		{
			((MonoBehaviour)NotYourLootPlugin.Instance).StartCoroutine(AudioPatches.DelayedEpicMmoScan());
		}
	}
}
internal static class EpicLootChestProvider
{
	private const string ProviderId = "NotYourFriend.NotYourLoot.ValheimPlusCraftFromChests";

	private static ManualLogSource _log;

	private static Assembly _valheimPlusAssembly;

	private static Type _inventoryAssistantType;

	private static Type _configurationType;

	private static MethodInfo _getNearbyChests;

	private static MethodInfo _removeNearbyItemsByName;

	private static bool _registered;

	private static bool _loggedMissingValheimPlus;

	private static bool _loggedReflectionFailure;

	private static EnchantUI _enchantUI;

	private static AugmentUI _augmentUI;

	private static ConvertUI _convertUI;

	private static UpgradeTableUI _upgradeUI;

	private static RuneUI _runeUI;

	private static SacrificeUI _sacrificeUI;

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

	private static float _chestCountCacheExpiresAt;

	private static int _chestCountCacheTargetId;

	private static float _chestCountCacheRange;

	private static bool _chestCountCacheCheckWard;

	private const float ChestCountCacheLifetime = 0.5f;

	private static float _queryContextExpiresAt;

	private static bool _queryContextValid;

	private static float _queryRange;

	private static bool _queryCheckWard;

	private static GameObject _queryTarget;

	private const float QueryContextLifetime = 0.5f;

	internal static void Initialize(ManualLogSource log)
	{
		_log = log;
		API.UnregisterInventoryProvider("NotYourFriend.NotYourLoot.ValheimPlusCraftFromChests");
		_registered = API.RegisterInventoryProvider("NotYourFriend.NotYourLoot.ValheimPlusCraftFromChests", (Func<List<ItemData>>)null, (Func<string, int>)CountChestItems, (Func<string, int, int>)RemoveChestItems, (Func<ItemData, int, int>)null);
		if (_registered)
		{
			ManualLogSource log2 = _log;
			if (log2 != null)
			{
				log2.LogInfo((object)"[EpicLoot/V+] Registered chest-backed material provider.");
			}
		}
		else
		{
			ManualLogSource log3 = _log;
			if (log3 != null)
			{
				log3.LogWarning((object)"[EpicLoot/V+] Failed to register inventory provider.");
			}
		}
	}

	private static int CountChestItems(string itemName)
	{
		if (string.IsNullOrEmpty(itemName))
		{
			return 0;
		}
		try
		{
			if (!TryGetCachedQueryContext(out var target, out var range, out var checkWard))
			{
				return 0;
			}
			EnsureChestCountSnapshot(target, range, checkWard);
			int value;
			return _chestCountCache.TryGetValue(itemName, out value) ? Math.Max(0, value) : 0;
		}
		catch (Exception ex)
		{
			LogReflectionFailure("counting nearby chest materials", ex);
			return 0;
		}
	}

	private static bool TryGetCachedQueryContext(out GameObject target, out float range, out bool checkWard)
	{
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (realtimeSinceStartup < _queryContextExpiresAt)
		{
			target = _queryTarget;
			range = _queryRange;
			checkWard = _queryCheckWard;
			if (_queryContextValid)
			{
				return (Object)(object)target != (Object)null;
			}
			return false;
		}
		_queryContextExpiresAt = realtimeSinceStartup + 0.5f;
		_queryContextValid = false;
		_queryTarget = null;
		_queryRange = 0f;
		_queryCheckWard = true;
		if (!IsChestContextAllowed())
		{
			target = null;
			range = 0f;
			checkWard = true;
			return false;
		}
		if (!TryGetValheimPlusSettings(out var range2, out var checkWard2, out var checkFromWorkbench))
		{
			target = null;
			range = 0f;
			checkWard = true;
			return false;
		}
		GameObject searchTarget = GetSearchTarget(checkFromWorkbench);
		if ((Object)(object)searchTarget == (Object)null)
		{
			target = null;
			range = 0f;
			checkWard = true;
			return false;
		}
		_queryTarget = searchTarget;
		_queryRange = range2;
		_queryCheckWard = checkWard2;
		_queryContextValid = true;
		target = _queryTarget;
		range = _queryRange;
		checkWard = _queryCheckWard;
		return true;
	}

	private static void EnsureChestCountSnapshot(GameObject target, float range, bool checkWard)
	{
		int instanceID = ((Object)target).GetInstanceID();
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (realtimeSinceStartup < _chestCountCacheExpiresAt && _chestCountCacheTargetId == instanceID && Mathf.Approximately(_chestCountCacheRange, range) && _chestCountCacheCheckWard == checkWard)
		{
			return;
		}
		_chestCountCache.Clear();
		if (_getNearbyChests.Invoke(null, new object[4] { target, range, checkWard, true }) is IEnumerable enumerable)
		{
			foreach (object item in enumerable)
			{
				Container val = (Container)((item is Container) ? item : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Inventory inventory = val.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				List<ItemData> allItems = inventory.GetAllItems();
				if (allItems == null)
				{
					continue;
				}
				foreach (ItemData item2 in allItems)
				{
					if (item2 != null && item2.m_shared != null && !string.IsNullOrEmpty(item2.m_shared.m_name) && item2.m_stack > 0)
					{
						string name = item2.m_shared.m_name;
						if (_chestCountCache.TryGetValue(name, out var value))
						{
							_chestCountCache[name] = value + item2.m_stack;
						}
						else
						{
							_chestCountCache[name] = item2.m_stack;
						}
					}
				}
			}
		}
		_chestCountCacheTargetId = instanceID;
		_chestCountCacheRange = range;
		_chestCountCacheCheckWard = checkWard;
		_chestCountCacheExpiresAt = realtimeSinceStartup + 0.5f;
	}

	private static void InvalidateChestCountSnapshot()
	{
		_chestCountCacheExpiresAt = 0f;
		_chestCountCache.Clear();
		_queryContextExpiresAt = 0f;
		_queryContextValid = false;
		_queryTarget = null;
	}

	private static int RemoveChestItems(string itemName, int amount)
	{
		if (string.IsNullOrEmpty(itemName) || amount <= 0 || !IsChestContextAllowed())
		{
			return 0;
		}
		if (!TryGetValheimPlusSettings(out var range, out var checkWard, out var checkFromWorkbench))
		{
			return 0;
		}
		GameObject searchTarget = GetSearchTarget(checkFromWorkbench);
		if ((Object)(object)searchTarget == (Object)null)
		{
			return 0;
		}
		try
		{
			object obj = _removeNearbyItemsByName.Invoke(null, new object[5] { searchTarget, range, itemName, amount, checkWard });
			if (obj == null)
			{
				return 0;
			}
			int num = Mathf.Clamp(Convert.ToInt32(obj), 0, amount);
			if (num > 0)
			{
				InvalidateChestCountSnapshot();
			}
			return num;
		}
		catch (Exception ex)
		{
			LogReflectionFailure("removing nearby chest materials", ex);
			return 0;
		}
	}

	internal static bool IsChestContextAllowed()
	{
		if (IsPanelActive<EnchantUI>(ref _enchantUI))
		{
			return true;
		}
		if (IsPanelActive<AugmentUI>(ref _augmentUI))
		{
			return true;
		}
		if (IsPanelActive<ConvertUI>(ref _convertUI))
		{
			return true;
		}
		if (IsPanelActive<UpgradeTableUI>(ref _upgradeUI))
		{
			return true;
		}
		if (IsPanelActive<RuneUI>(ref _runeUI))
		{
			return true;
		}
		if (IsPanelActive<SacrificeUI>(ref _sacrificeUI))
		{
			if ((Object)(object)_sacrificeUI.IdentifyToggle != (Object)null)
			{
				return _sacrificeUI.IdentifyToggle.isOn;
			}
			return false;
		}
		return false;
	}

	private static bool IsPanelActive<T>(ref T cached) where T : MonoBehaviour
	{
		if ((Object)(object)cached != (Object)null && ((Behaviour)cached).isActiveAndEnabled && ((Component)cached).gameObject.activeInHierarchy)
		{
			return true;
		}
		T[] array = Resources.FindObjectsOfTypeAll<T>();
		foreach (T val in array)
		{
			if (!((Object)(object)val == (Object)null) && ((Behaviour)(object)val).isActiveAndEnabled && ((Component)(object)val).gameObject.activeInHierarchy)
			{
				cached = val;
				return true;
			}
		}
		return false;
	}

	private static bool TryResolveValheimPlus()
	{
		if (_valheimPlusAssembly != null && _inventoryAssistantType != null && _configurationType != null && _getNearbyChests != null && _removeNearbyItemsByName != null)
		{
			return true;
		}
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			if (string.Equals(assembly.GetName().Name, "ValheimPlus", StringComparison.OrdinalIgnoreCase))
			{
				_valheimPlusAssembly = assembly;
				break;
			}
		}
		if (_valheimPlusAssembly == null)
		{
			if (!_loggedMissingValheimPlus)
			{
				_loggedMissingValheimPlus = true;
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)"[EpicLoot/V+] ValheimPlus is not loaded; chest-backed Epic Loot costs are disabled.");
				}
			}
			return false;
		}
		try
		{
			_inventoryAssistantType = _valheimPlusAssembly.GetType("ValheimPlus.InventoryAssistant", throwOnError: false);
			_configurationType = _valheimPlusAssembly.GetType("ValheimPlus.Configurations.Configuration", throwOnError: false);
			if (_inventoryAssistantType == null || _configurationType == null)
			{
				return false;
			}
			_getNearbyChests = _inventoryAssistantType.GetMethod("GetNearbyChests", BindingFlags.Static | BindingFlags.Public, null, new Type[4]
			{
				typeof(GameObject),
				typeof(float),
				typeof(bool),
				typeof(bool)
			}, null);
			_removeNearbyItemsByName = _inventoryAssistantType.GetMethod("RemoveItemInAmountFromAllNearbyChests", BindingFlags.Static | BindingFlags.Public, null, new Type[5]
			{
				typeof(GameObject),
				typeof(float),
				typeof(string),
				typeof(int),
				typeof(bool)
			}, null);
			return _getNearbyChests != null && _removeNearbyItemsByName != null;
		}
		catch (Exception ex)
		{
			LogReflectionFailure("resolving ValheimPlus", ex);
			return false;
		}
	}

	private static bool TryGetValheimPlusSettings(out float range, out bool checkWard, out bool checkFromWorkbench)
	{
		range = 0f;
		checkWard = true;
		checkFromWorkbench = false;
		if (!TryResolveValheimPlus())
		{
			return false;
		}
		try
		{
			object staticMemberValue = GetStaticMemberValue(_configurationType, "Current");
			if (staticMemberValue == null)
			{
				return false;
			}
			object instanceMemberValue = GetInstanceMemberValue(staticMemberValue, "CraftFromChest");
			if (instanceMemberValue == null)
			{
				return false;
			}
			if (!ReadBool(instanceMemberValue, "IsEnabled", fallback: false))
			{
				return false;
			}
			range = Mathf.Clamp(ReadFloat(instanceMemberValue, "range", 10f), 1f, 50f);
			bool flag = ReadBool(instanceMemberValue, "ignorePrivateAreaCheck", fallback: false);
			checkWard = !flag;
			checkFromWorkbench = ReadBool(instanceMemberValue, "checkFromWorkbench", fallback: false);
			return true;
		}
		catch (Exception ex)
		{
			LogReflectionFailure("reading ValheimPlus CraftFromChest settings", ex);
			return false;
		}
	}

	private static GameObject GetSearchTarget(bool checkFromWorkbench)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return null;
		}
		if (checkFromWorkbench)
		{
			CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation != (Object)null)
			{
				return ((Component)currentCraftingStation).gameObject;
			}
		}
		return ((Component)localPlayer).gameObject;
	}

	private static object GetStaticMemberValue(Type type, string name)
	{
		PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
		if (property != null)
		{
			return property.GetValue(null);
		}
		return type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.GetValue(null);
	}

	private static object GetInstanceMemberValue(object instance, string name)
	{
		if (instance == null)
		{
			return null;
		}
		Type type = instance.GetType();
		PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
		if (property != null)
		{
			return property.GetValue(instance);
		}
		return type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.GetValue(instance);
	}

	private static bool ReadBool(object instance, string name, bool fallback)
	{
		object instanceMemberValue = GetInstanceMemberValue(instance, name);
		if (instanceMemberValue == null)
		{
			return fallback;
		}
		try
		{
			return Convert.ToBoolean(instanceMemberValue);
		}
		catch
		{
			return fallback;
		}
	}

	private static float ReadFloat(object instance, string name, float fallback)
	{
		object instanceMemberValue = GetInstanceMemberValue(instance, name);
		if (instanceMemberValue == null)
		{
			return fallback;
		}
		try
		{
			return Convert.ToSingle(instanceMemberValue);
		}
		catch
		{
			return fallback;
		}
	}

	private static void LogReflectionFailure(string action, Exception ex)
	{
		if (!_loggedReflectionFailure)
		{
			_loggedReflectionFailure = true;
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogWarning((object)$"[EpicLoot/V+] Error while {action}: {ex}");
			}
		}
	}
}
[HarmonyPatch(typeof(EnchantingTableUIPanelBase), "LocalPlayerCanAffordCost")]
internal static class EpicLootChestAffordabilityPatch
{
	private static void Postfix(List<InventoryItemListElement> cost, ref bool __result)
	{
		if (!__result && cost != null && EpicLootChestProvider.IsChestContextAllowed() && CanAffordThroughEpicLootInventory(cost))
		{
			__result = true;
		}
	}

	internal static bool CanAffordThroughEpicLootInventory(List<InventoryItemListElement> cost)
	{
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		foreach (InventoryItemListElement item2 in cost)
		{
			if (item2 == null)
			{
				continue;
			}
			ItemData item = item2.GetItem();
			if (item != null && item.m_shared != null && !string.IsNullOrEmpty(item.m_shared.m_name))
			{
				string name = item.m_shared.m_name;
				if (!dictionary.ContainsKey(name))
				{
					dictionary[name] = 0;
				}
				dictionary[name] += Math.Max(0, item.m_stack);
			}
		}
		foreach (KeyValuePair<string, int> item3 in dictionary)
		{
			if (InventoryManagement.Instance.CountItem(item3.Key) < item3.Value)
			{
				return false;
			}
		}
		return true;
	}
}
[HarmonyPatch(typeof(SacrificeUI), "LocalPlayerCanAffordIdentifyCost")]
internal static class EpicLootIdentifyChestAffordabilityPatch
{
	private static void Postfix(List<InventoryItemListElement> cost, ref bool __result)
	{
		if (!__result && cost != null && EpicLootChestProvider.IsChestContextAllowed() && EpicLootChestAffordabilityPatch.CanAffordThroughEpicLootInventory(cost))
		{
			__result = true;
		}
	}
}
internal static class EpicLootCostDisplay
{
	[HarmonyPatch(typeof(MultiSelectItemListElement), "SetItem")]
	private static class MultiSelectItemListElement_SetItem_Patch
	{
		private static void Postfix(MultiSelectItemListElement __instance, IListElement item)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)__instance == (Object)null) && item != null && !((Object)(object)__instance.ItemTotalQuantity == (Object)null) && IsCostListElement(__instance))
				{
					ItemData item2 = item.GetItem();
					if (item2 != null && item2.m_shared != null && !string.IsNullOrEmpty(item2.m_shared.m_name))
					{
						int num = Math.Max(0, item2.m_stack);
						int val = InventoryManagement.Instance.CountItem(item2.m_shared.m_name);
						val = Math.Max(0, val);
						__instance.ItemTotalQuantity.text = val + "/" + num;
						((Graphic)__instance.ItemTotalQuantity).color = ((val < num) ? Color.red : Color.white);
					}
				}
			}
			catch
			{
			}
		}
	}

	private static bool IsCostListElement(MultiSelectItemListElement element)
	{
		Transform transform = ((Component)element).transform;
		MonoBehaviour[] array = Resources.FindObjectsOfTypeAll<MonoBehaviour>();
		foreach (MonoBehaviour val in array)
		{
			if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled || !((Component)val).gameObject.activeInHierarchy)
			{
				continue;
			}
			Type type = ((object)val).GetType();
			string text = type.FullName ?? type.Name;
			if (!text.Contains("EpicLoot") && !text.Contains("EpicLoot_UnityLib"))
			{
				continue;
			}
			object memberValue = GetMemberValue(val, "CostList");
			Component val2 = (Component)((memberValue is Component) ? memberValue : null);
			if (!((Object)(object)val2 == (Object)null))
			{
				Transform transform2 = val2.transform;
				if ((Object)(object)transform == (Object)(object)transform2 || transform.IsChildOf(transform2))
				{
					return true;
				}
			}
		}
		return false;
	}

	private static object GetMemberValue(object instance, string name)
	{
		if (instance == null)
		{
			return null;
		}
		Type type = instance.GetType();
		while (type != null)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(instance);
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				return property.GetValue(instance);
			}
			type = type.BaseType;
		}
		return null;
	}
}
[HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[]
{
	typeof(StatusEffect),
	typeof(bool),
	typeof(int),
	typeof(float),
	typeof(short)
})]
internal static class EpicMmoOrbAudioPatch
{
	private static AudioClip _bonusClip;

	private static AudioSource _bonusSource;

	private static void Postfix(StatusEffect __result)
	{
		if (!((Object)(object)__result == (Object)null) && string.Equals(((Object)__result).name, "MMO_XP", StringComparison.Ordinal))
		{
			PlayBonusSound();
		}
	}

	private static void PlayBonusSound()
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Expected O, but got Unknown
		if (NotYourLootPlugin.AudioEpicMmoEnabled == null || !NotYourLootPlugin.AudioEpicMmoEnabled.Value)
		{
			return;
		}
		try
		{
			if ((Object)(object)_bonusClip == (Object)null)
			{
				_bonusClip = AudioPatches.LoadPcmWav(Path.Combine(Paths.PluginPath, "NotYourFriend-NotYourLoot", "Bonus.wav"), "NotYourLoot_EpicMmoOrbBonus", "EpicMMO XP-orb replacement");
			}
			if ((Object)(object)_bonusClip == (Object)null)
			{
				NotYourLootPlugin.LogWarning("Bonus.wav could not be loaded for EpicMMO XP orbs.");
				return;
			}
			if ((Object)(object)_bonusSource == (Object)null)
			{
				GameObject val = new GameObject("NotYourLoot_EpicMmoOrbBonus");
				Object.DontDestroyOnLoad((Object)val);
				_bonusSource = val.AddComponent<AudioSource>();
				_bonusSource.playOnAwake = false;
				_bonusSource.loop = false;
				_bonusSource.spatialBlend = 0f;
				AudioPatches.RouteToValheimGuiMixer(_bonusSource);
			}
			float num = Mathf.Clamp01(NotYourLootPlugin.AudioEpicMmoVolume.Value);
			_bonusSource.PlayOneShot(_bonusClip, num);
			NotYourLootPlugin.LogInfo("EpicMMO MMO_XP applied: played Bonus.wav " + $"at volume {num:0.###}.");
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("EpicMMO XP-orb replacement sound failed: " + ex.Message);
		}
	}
}
internal static class HerbalistVegetationPatch
{
	private static ConfigEntry<bool> Enabled;

	private static ConfigEntry<float> SpawnChance;

	private static ConfigEntry<int> MinGroup;

	private static ConfigEntry<int> MaxGroup;

	private static ConfigEntry<bool> CleanNames;

	private static readonly HashSet<string> HerbalistPlants = new HashSet<string>(StringComparer.Ordinal)
	{
		"BH_Pickable_Boswellia", "BH_Pickable_Chamomile", "BH_Pickable_Chicory", "BH_Pickable_Daisy", "BH_Pickable_Echinacea", "BH_Pickable_Lavender", "BH_Pickable_AslaugsHerb", "BH_Pickable_Bjorncap", "BH_Pickable_HelshadeFungus", "BH_Pickable_LokisTrickcap",
		"BH_Pickable_NordicFirebloom", "BH_Pickable_SeidrBlossoms", "BH_Pickable_SkaldsIvy", "BH_Pickable_ThorsToadstool", "BH_Pickable_Valhallaberry", "BH_Pickable_ValkyrieFern", "BH_Pickable_VikingsBreadcap"
	};

	internal static void BindConfig(ConfigFile config)
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Expected O, but got Unknown
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Expected O, but got Unknown
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Expected O, but got Unknown
		Enabled = config.Bind<bool>("Herbalist - Vegetation", "Enabled", true, "Reduce naturally generated Herbalist vegetation.");
		SpawnChance = config.Bind<float>("Herbalist - Vegetation", "SpawnChance", 0.5f, new ConfigDescription("Probability of one group attempt per eligible zone.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.99f), Array.Empty<object>()));
		MinGroup = config.Bind<int>("Herbalist - Vegetation", "MinimumGroupSize", 1, new ConfigDescription("Minimum plants attempted per group.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
		MaxGroup = config.Bind<int>("Herbalist - Vegetation", "MaximumGroupSize", 3, new ConfigDescription("Maximum plants attempted per group.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
		CleanNames = config.Bind<bool>("Herbalist - Names", "Enabled", true, "Use localized names for wild Herbalist plants.");
	}

	internal static void Install(Harmony harmony)
	{
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("Herbalist.Functions.VegetationSetup");
		if (type == null)
		{
			NotYourLootPlugin.LogInfo("[Herbalist] Not installed; scarcity patch skipped.");
			return;
		}
		MethodInfo methodInfo = AccessTools.Method(type, "Init", new Type[1] { typeof(ZoneSystem) }, (Type[])null);
		if (methodInfo == null)
		{
			NotYourLootPlugin.LogWarning("[Herbalist] VegetationSetup.Init not found.");
			return;
		}
		try
		{
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(HerbalistVegetationPatch), "AfterVegetationInit", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			NotYourLootPlugin.LogInfo("[Herbalist] Vegetation scarcity patch installed.");
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("[Herbalist] Could not install patch: " + ex);
		}
	}

	private static void AfterVegetationInit(ZoneSystem zoneSystem)
	{
		if ((Object)(object)zoneSystem == (Object)null)
		{
			return;
		}
		bool flag = Enabled != null && Enabled.Value;
		bool flag2 = CleanNames != null && CleanNames.Value;
		if (!flag && !flag2)
		{
			return;
		}
		int num = Math.Max(1, MinGroup.Value);
		int num2 = Math.Max(num, MaxGroup.Value);
		int num3 = 0;
		int num4 = 0;
		foreach (ZoneVegetation item in zoneSystem.m_vegetation)
		{
			if ((Object)(object)item?.m_prefab == (Object)null)
			{
				continue;
			}
			string name = ((Object)item.m_prefab).name;
			if (!HerbalistPlants.Contains(name))
			{
				continue;
			}
			if (flag)
			{
				item.m_max = SpawnChance.Value;
				item.m_groupSizeMin = num;
				item.m_groupSizeMax = num2;
				num3++;
			}
			if (flag2)
			{
				Pickable component = item.m_prefab.GetComponent<Pickable>();
				if ((Object)(object)component != (Object)null)
				{
					string overrideName = "$bh_" + name.Substring("BH_Pickable_".Length).ToLowerInvariant();
					component.m_overrideName = overrideName;
					num4++;
				}
			}
		}
		if (flag && num3 != HerbalistPlants.Count)
		{
			NotYourLootPlugin.LogWarning($"[Herbalist] Adjusted {num3}/" + $"{HerbalistPlants.Count} expected vegetation entries.");
		}
		else if (flag)
		{
			NotYourLootPlugin.LogInfo($"[Herbalist] Adjusted {num3} vegetation entries: " + $"chance={SpawnChance.Value:F2}, " + $"group={num}-{num2}.");
		}
		if (flag2)
		{
			NotYourLootPlugin.LogInfo($"[Herbalist] Localized {num4}/" + $"{HerbalistPlants.Count} wild plant names.");
		}
	}
}
internal static class ModdedContainerCompatibility
{
	private static readonly HashSet<Container> Containers = new HashSet<Container>();

	private static bool _installed;

	private static readonly FieldInfo LastRevisionField = AccessTools.Field(typeof(Container), "m_lastRevision");

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

	internal static void Install(Harmony harmony)
	{
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Expected O, but got Unknown
		if (_installed || harmony == null)
		{
			return;
		}
		try
		{
			Type type = AccessTools.TypeByName("ValheimPlus.InventoryAssistant");
			if (type == null)
			{
				NotYourLootPlugin.LogWarning("[Containers] ValheimPlus InventoryAssistant not found.");
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "GetNearbyChests", new Type[4]
			{
				typeof(GameObject),
				typeof(float),
				typeof(bool),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo == null)
			{
				NotYourLootPlugin.LogWarning("[Containers] ValheimPlus GetNearbyChests method not found.");
				return;
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ModdedContainerCompatibility), "GetNearbyChestsPostfix", (Type[])null, (Type[])null);
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_installed = true;
			NotYourLootPlugin.LogInfo("[Containers] Installed modded-container compatibility for ValheimPlus Craft From Chest.");
		}
		catch (Exception arg)
		{
			NotYourLootPlugin.LogWarning($"[Containers] Failed to install ValheimPlus compatibility: {arg}");
		}
	}

	internal static void Register(Container container)
	{
		if ((Object)(object)container != (Object)null)
		{
			Containers.Add(container);
		}
	}

	internal static void Unregister(Container container)
	{
		if ((Object)(object)container != (Object)null)
		{
			Containers.Remove(container);
		}
	}

	private static void GetNearbyChestsPostfix(GameObject target, float range, bool checkWard, bool includeVehicles, ref List<Container> __result)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)target == (Object)null || range <= 0f)
			{
				return;
			}
			if (__result == null)
			{
				__result = new List<Container>();
			}
			long playerID = localPlayer.GetPlayerID();
			Vector3 position = target.transform.position;
			Containers.RemoveWhere((Container container) => (Object)(object)container == (Object)null);
			Container[] array = (Container[])(object)new Container[Containers.Count];
			Containers.CopyTo(array);
			Container[] array2 = array;
			foreach (Container val in array2)
			{
				if ((Object)(object)val == (Object)null || __result.Contains(val) || (LastRevisionField != null && LastRevisionField.GetValue(val) is uint num2 && num2 == uint.MaxValue) || val.GetInventory() == null)
				{
					continue;
				}
				Transform transform = ((Component)val).transform;
				if ((Object)(object)transform == (Object)null)
				{
					continue;
				}
				Vector3 position2 = transform.position;
				if (Vector3.Distance(position, position2) >= range)
				{
					continue;
				}
				if (CheckAccessMethod != null)
				{
					object obj = CheckAccessMethod.Invoke(val, new object[1] { playerID });
					if (obj is bool && !(bool)obj)
					{
						continue;
					}
				}
				if ((!checkWard || PrivateArea.CheckAccess(position2, 0f, false, true)) && !HasVehicleInHierarchy(val))
				{
					Piece val2 = FindAssociatedPiece(val);
					if (!((Object)(object)val2 == (Object)null) && val2.IsPlacedByPlayer())
					{
						__result.Add(val);
					}
				}
			}
		}
		catch (Exception arg)
		{
			NotYourLootPlugin.LogWarning($"[Containers] Supplemental nearby-container scan failed: {arg}");
		}
	}

	private static Piece FindAssociatedPiece(Container container)
	{
		if ((Object)(object)container == (Object)null)
		{
			return null;
		}
		Piece componentInParent = ((Component)container).GetComponentInParent<Piece>();
		if ((Object)(object)componentInParent != (Object)null)
		{
			return componentInParent;
		}
		Transform root = ((Component)container).transform.root;
		if ((Object)(object)root == (Object)null)
		{
			return null;
		}
		return ((Component)root).GetComponentInChildren<Piece>(true);
	}

	private static bool HasVehicleInHierarchy(Container container)
	{
		if ((Object)(object)container == (Object)null)
		{
			return false;
		}
		if ((Object)(object)((Component)container).GetComponentInParent<Vagon>() != (Object)null || (Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null)
		{
			return true;
		}
		Transform root = ((Component)container).transform.root;
		if ((Object)(object)root == (Object)null)
		{
			return false;
		}
		if (!((Object)(object)((Component)root).GetComponentInChildren<Vagon>(true) != (Object)null))
		{
			return (Object)(object)((Component)root).GetComponentInChildren<Ship>(true) != (Object)null;
		}
		return true;
	}
}
[HarmonyPatch(typeof(Container), "Awake")]
internal static class ModdedContainerRegisterPatch
{
	private static void Postfix(Container __instance)
	{
		ModdedContainerCompatibility.Register(__instance);
	}
}
[BepInPlugin("NotYourFriend.NotYourLoot", "NotYourLoot", "0.7.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class NotYourLootPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "NotYourFriend.NotYourLoot";

	public const string PluginName = "NotYourLoot";

	public const string PluginVersion = "0.7.1";

	internal static NotYourLootPlugin Instance;

	private Harmony _harmony;

	internal static ConfigEntry<bool> AudioEpicMmoEnabled;

	internal static ConfigEntry<float> AudioEpicMmoVolume;

	internal static ConfigEntry<bool> AudioCaptainEnabled;

	internal static ConfigEntry<float> AudioCaptainVolume;

	internal static ConfigEntry<bool> AudioCaptainQuestEnabled;

	internal static ConfigEntry<float> AudioCaptainQuestVolume;

	internal static ConfigEntry<float> AudioCaptainLevelUpVolume;

	private void Awake()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		Instance = this;
		BindAudioConfig();
		HerbalistVegetationPatch.BindConfig(((BaseUnityPlugin)this).Config);
		EpicLootChestProvider.Initialize(((BaseUnityPlugin)this).Logger);
		_harmony = new Harmony("NotYourFriend.NotYourLoot");
		_harmony.PatchAll();
		ModdedContainerCompatibility.Install(_harmony);
		AudioPatches.Install(_harmony);
		HerbalistVegetationPatch.Install(_harmony);
		((MonoBehaviour)this).StartCoroutine(AudioPatches.DelayedEpicMmoScan());
		((MonoBehaviour)this).StartCoroutine(AudioPatches.DelayedEpicMmoOrbEffectCleanup());
		((BaseUnityPlugin)this).Logger.LogInfo((object)"NotYourLoot 0.7.1 loaded successfully.");
	}

	private void BindAudioConfig()
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Expected O, but got Unknown
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Expected O, but got Unknown
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Expected O, but got Unknown
		AudioEpicMmoEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio - EpicMMO", "Enabled", true, "Normalize audio embedded in EpicMMO GameObject assets.");
		AudioEpicMmoVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio - EpicMMO", "VolumeMultiplier", 0.5f, new ConfigDescription("Multiplier applied to EpicMMO AudioSource volume. 1.0 leaves the original volume unchanged.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
		AudioCaptainEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio - CaptainSkillTree", "Enabled", true, "Normalize CaptainSkillTree VFX/SFX audio and supported dedicated sound managers.");
		AudioCaptainVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio - CaptainSkillTree", "VolumeMultiplier", 0.3f, new ConfigDescription("Multiplier applied to general CaptainSkillTree audio. 1.0 leaves the original volume unchanged.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
		AudioCaptainQuestEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio - CaptainSkillTree Quest Complete", "Enabled", true, "Apply a separate multiplier to CaptainSkillTree quest-complete music.");
		AudioCaptainQuestVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio - CaptainSkillTree Quest Complete", "VolumeMultiplier", 0.15f, new ConfigDescription("Multiplier applied to quest-complete music before CaptainSkillTree plays it. 1.0 leaves the configured quest volume unchanged.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
		AudioCaptainLevelUpVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio - CaptainSkillTree Level Up", "Volume", 0.65f, new ConfigDescription("Volume used for the LockChime.wav replacement when CaptainSkillTree levels up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
	}

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

	internal static void LogInfo(string message)
	{
		NotYourLootPlugin instance = Instance;
		if (instance != null)
		{
			((BaseUnityPlugin)instance).Logger.LogInfo((object)message);
		}
	}

	internal static void LogWarning(string message)
	{
		NotYourLootPlugin instance = Instance;
		if (instance != null)
		{
			((BaseUnityPlugin)instance).Logger.LogWarning((object)message);
		}
	}
}
internal static class TherzieEquipment
{
	private enum TherzieSource
	{
		None,
		Warfare,
		Wizardry,
		Armory
	}

	private sealed class OwnedItemInfo
	{
		internal TherzieSource Source;

		internal string PrefabName;

		internal string ArmoryGroup;
	}

	private static readonly Dictionary<ItemDrop, OwnedItemInfo> OwnedItems = new Dictionary<ItemDrop, OwnedItemInfo>();

	private static bool _cacheBuilt;

	private static readonly HashSet<ItemType> EquipmentTypes = new HashSet<ItemType>
	{
		(ItemType)3,
		(ItemType)4,
		(ItemType)5,
		(ItemType)6,
		(ItemType)7,
		(ItemType)11,
		(ItemType)12,
		(ItemType)14,
		(ItemType)17,
		(ItemType)18,
		(ItemType)19,
		(ItemType)22,
		(ItemType)24
	};

	private static readonly string[] ArmorySets = new string[10] { "Razorback", "Hunter", "Leather", "Rogue", "Vigorous", "Warrior", "Vidar", "Fenrir", "Bold", "Legion" };

	private static readonly string[] ArmoryTiers = new string[5] { "Iron", "Silver", "BM", "Carapace", "Flametal" };

	internal static bool IsTargetEquipment(Recipe recipe)
	{
		OwnedItemInfo info;
		return TryGetInfo(recipe, out info);
	}

	internal static bool IsUnlocked(Recipe recipe, HashSet<string> knownMaterials)
	{
		if (knownMaterials == null || !TryGetInfo(recipe, out var info))
		{
			return false;
		}
		if (info.Source != TherzieSource.Armory || string.IsNullOrEmpty(info.ArmoryGroup))
		{
			return knownMaterials.Contains(recipe.m_item.m_itemData.m_shared.m_name);
		}
		foreach (KeyValuePair<ItemDrop, OwnedItemInfo> ownedItem in OwnedItems)
		{
			ItemDrop key = ownedItem.Key;
			OwnedItemInfo value = ownedItem.Value;
			if (value.Source == TherzieSource.Armory && !(value.ArmoryGroup != info.ArmoryGroup) && !((Object)(object)key == (Object)null) && key.m_itemData != null && key.m_itemData.m_shared != null && knownMaterials.Contains(key.m_itemData.m_shared.m_name))
			{
				return true;
			}
		}
		return false;
	}

	internal static string GetUnlockDescription(Recipe recipe)
	{
		if (!TryGetInfo(recipe, out var info))
		{
			return "unknown";
		}
		if (info.Source == TherzieSource.Armory && !string.IsNullOrEmpty(info.ArmoryGroup))
		{
			return "Armory group " + info.ArmoryGroup;
		}
		return $"{info.Source} exact item";
	}

	private static bool TryGetInfo(Recipe recipe, out OwnedItemInfo info)
	{
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		info = null;
		if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null || recipe.m_item.m_itemData == null || recipe.m_item.m_itemData.m_shared == null)
		{
			return false;
		}
		EnsureCache();
		if (!OwnedItems.TryGetValue(recipe.m_item, out info))
		{
			return false;
		}
		return EquipmentTypes.Contains(recipe.m_item.m_itemData.m_shared.m_itemType);
	}

	private static void EnsureCache()
	{
		if (_cacheBuilt && OwnedItems.Count > 0)
		{
			return;
		}
		OwnedItems.Clear();
		AddItemsFromAssembly("Warfare", TherzieSource.Warfare);
		AddItemsFromAssembly("Wizardry", TherzieSource.Wizardry);
		AddItemsFromAssembly("Armory", TherzieSource.Armory);
		if (OwnedItems.Count <= 0)
		{
			return;
		}
		if (!_cacheBuilt)
		{
			int num = OwnedItems.Values.Count((OwnedItemInfo x) => x.Source == TherzieSource.Warfare);
			int num2 = OwnedItems.Values.Count((OwnedItemInfo x) => x.Source == TherzieSource.Wizardry);
			int num3 = OwnedItems.Values.Count((OwnedItemInfo x) => x.Source == TherzieSource.Armory);
			int num4 = OwnedItems.Values.Count((OwnedItemInfo x) => x.Source == TherzieSource.Armory && !string.IsNullOrEmpty(x.ArmoryGroup));
			NotYourLootPlugin.LogInfo("Therzie ownership cache built: " + $"{OwnedItems.Count} items " + $"(Warfare {num}, " + $"Wizardry {num2}, " + $"Armory {num3}, " + $"Armory grouped {num4}).");
		}
		_cacheBuilt = true;
	}

	private static void AddItemsFromAssembly(string assemblyName, TherzieSource source)
	{
		try
		{
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, assemblyName, StringComparison.OrdinalIgnoreCase));
			if (assembly == null)
			{
				return;
			}
			Type type = assembly.GetType("ItemManager.Item");
			if (type == null)
			{
				return;
			}
			FieldInfo field = type.GetField("itemDropMap", BindingFlags.Static | BindingFlags.NonPublic);
			if (field == null || !(field.GetValue(null) is IDictionary dictionary))
			{
				return;
			}
			foreach (DictionaryEntry item in dictionary)
			{
				object key = item.Key;
				ItemDrop val = (ItemDrop)((key is ItemDrop) ? key : null);
				if (val != null)
				{
					string prefabName = (((Object)(object)((Component)val).gameObject != (Object)null) ? ((Object)((Component)val).gameObject).name : string.Empty);
					OwnedItems[val] = new OwnedItemInfo
					{
						Source = source,
						PrefabName = prefabName,
						ArmoryGroup = ((source == TherzieSource.Armory) ? GetArmoryGroup(prefabName) : null)
					};
				}
			}
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("Could not inspect " + assemblyName + ": " + ex.Message);
		}
	}

	private static string GetArmoryGroup(string prefabName)
	{
		if (string.IsNullOrEmpty(prefabName))
		{
			return null;
		}
		string text = prefabName;
		if (text.EndsWith("_TW", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring(0, text.Length - 3);
		}
		string[] armorySets = ArmorySets;
		foreach (string text2 in armorySets)
		{
			string text3 = "Helmet" + text2;
			string[] armoryTiers;
			if (text.StartsWith(text3, StringComparison.OrdinalIgnoreCase))
			{
				string text4 = text.Substring(text3.Length);
				if (text4.Length == 0)
				{
					return text2;
				}
				armoryTiers = ArmoryTiers;
				foreach (string text5 in armoryTiers)
				{
					if (string.Equals(text4, text5, StringComparison.OrdinalIgnoreCase))
					{
						return text2 + text5;
					}
				}
				return null;
			}
			string text6 = "Armor" + text2;
			if (!text.StartsWith(text6, StringComparison.OrdinalIgnoreCase))
			{
				continue;
			}
			string a = text.Substring(text6.Length);
			if (string.Equals(a, "Chest", StringComparison.OrdinalIgnoreCase))
			{
				return text2;
			}
			armoryTiers = ArmoryTiers;
			foreach (string text7 in armoryTiers)
			{
				if (string.Equals(a, "Chest" + text7, StringComparison.OrdinalIgnoreCase) || string.Equals(a, text7 + "Chest", StringComparison.OrdinalIgnoreCase))
				{
					return text2 + text7;
				}
			}
			if (string.Equals(a, "Legs", StringComparison.OrdinalIgnoreCase))
			{
				return text2;
			}
			armoryTiers = ArmoryTiers;
			foreach (string text8 in armoryTiers)
			{
				if (string.Equals(a, "Legs" + text8, StringComparison.OrdinalIgnoreCase) || string.Equals(a, text8 + "Legs", StringComparison.OrdinalIgnoreCase))
				{
					return text2 + text8;
				}
			}
		}
		return null;
	}
}
[HarmonyPatch(typeof(Player), "AddKnownRecipe")]
internal static class AddKnownRecipePatch
{
	private static bool Prefix(Recipe recipe, HashSet<string> ___m_knownMaterial)
	{
		if (!TherzieEquipment.IsTargetEquipment(recipe))
		{
			return true;
		}
		return TherzieEquipment.IsUnlocked(recipe, ___m_knownMaterial);
	}
}
[HarmonyPatch(typeof(Player), "UpdateKnownRecipesList")]
internal static class UpdateKnownRecipesListPatch
{
	private static readonly MethodInfo AddKnownRecipeMethod = AccessTools.Method(typeof(Player), "AddKnownRecipe", (Type[])null, (Type[])null);

	private static void Postfix(Player __instance, HashSet<string> ___m_knownMaterial, HashSet<string> ___m_knownRecipes)
	{
		if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_recipes == null || AddKnownRecipeMethod == null)
		{
			return;
		}
		foreach (Recipe recipe in ObjectDB.instance.m_recipes)
		{
			if (TherzieEquipment.IsTargetEquipment(recipe) && TherzieEquipment.IsUnlocked(recipe, ___m_knownMaterial))
			{
				string name = recipe.m_item.m_itemData.m_shared.m_name;
				if (!___m_knownRecipes.Contains(name))
				{
					AddKnownRecipeMethod.Invoke(__instance, new object[1] { recipe });
					NotYourLootPlugin.LogInfo("Discovery unlocked recipe: " + name + " [" + TherzieEquipment.GetUnlockDescription(recipe) + "]");
				}
			}
		}
	}
}
[HarmonyPatch(typeof(HotkeyBar), "UpdateIcons")]
[HarmonyAfter(new string[] { "randyknapp.mods.equipmentandquickslots" })]
internal static class EquipmentAndQuickSlotsQuickBarUIPatch
{
	private const float MinimumLabelWidth = 100f;

	private static void Postfix(HotkeyBar __instance)
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)__instance == (Object)null || ((Object)((Component)__instance).gameObject).name != "QuickSlotsHotkeyBar")
		{
			return;
		}
		TMP_Text[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<TMP_Text>(true);
		foreach (TMP_Text val in componentsInChildren)
		{
			if (!((Object)(object)val == (Object)null) && !(((Object)((Component)val).gameObject).name != "binding"))
			{
				val.textWrappingMode = (TextWrappingModes)0;
				val.overflowMode = (TextOverflowModes)0;
				RectTransform rectTransform = val.rectTransform;
				Rect rect = rectTransform.rect;
				if (((Rect)(ref rect)).width < 100f)
				{
					rectTransform.SetSizeWithCurrentAnchors((Axis)0, 100f);
				}
			}
		}
	}
}
internal static class WorkerBonusPatches
{
	private enum WorkerBonusKind
	{
		None,
		Woodcutting,
		Gathering,
		Mining,
		Crafting
	}

	[HarmonyPatch]
	private static class ProductionContextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type type = AccessTools.TypeByName("CaptainSkillTree.SkillTree.ProductionEffects");
			if (type == null)
			{
				yield break;
			}
			string[] array = new string[4] { "TryDropExtraWood", "TryDropExtraResource", "TryDropExtraMiningResource", "CheckAndApplyCraftingBonus" };
			string[] array2 = array;
			foreach (string name in array2)
			{
				MethodInfo methodInfo = AccessTools.GetDeclaredMethods(type).FirstOrDefault((MethodInfo m) => m.Name == name);
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
			}
		}

		private static void Prefix(MethodBase __originalMethod, object[] __args)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			_activeKind = WorkerBonusKind.None;
			_activePosition = Vector3.zero;
			_vanillaEffectPlayed = false;
			switch (__originalMethod.Name)
			{
			case "TryDropExtraWood":
				_activeKind = WorkerBonusKind.Woodcutting;
				if (__args.Length >= 3 && __args[2] is Vector3 activePosition)
				{
					_activePosition = activePosition;
				}
				break;
			case "TryDropExtraResource":
				if (__args.Length >= 3 && __args[2] is string a && string.Equals(a, "gathering", StringComparison.OrdinalIgnoreCase))
				{
					_activeKind = WorkerBonusKind.Gathering;
					if (__args[1] is Vector3 activePosition3)
					{
						_activePosition = activePosition3;
					}
				}
				break;
			case "TryDropExtraMiningResource":
				_activeKind = WorkerBonusKind.Mining;
				if (__args.Length >= 2 && __args[1] is Vector3 activePosition2)
				{
					_activePosition = activePosition2;
				}
				break;
			case "CheckAndApplyCraftingBonus":
				_activeKind = WorkerBonusKind.Crafting;
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					_activePosition = ((Component)Player.m_localPlayer).transform.position;
				}
				break;
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_activeKind = WorkerBonusKind.None;
			_activePosition = Vector3.zero;
			_vanillaEffectPlayed = false;
			return __exception;
		}
	}

	[HarmonyPatch]
	private static class CaptainFloatingTextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type type = AccessTools.TypeByName("CaptainSkillTree.SkillTree.SkillEffect");
			if (type == null)
			{
				yield break;
			}
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				if (declaredMethod.Name == "DrawFloatingText")
				{
					yield return declaredMethod;
				}
			}
		}

		private static bool Prefix(object[] __args)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			if (_activeKind != WorkerBonusKind.Woodcutting && _activeKind != WorkerBonusKind.Gathering && _activeKind != WorkerBonusKind.Mining)
			{
				return true;
			}
			string text = null;
			for (int i = 0; i < __args.Length; i++)
			{
				if (__args[i] is string text2)
				{
					text = text2;
					break;
				}
			}
			if (string.IsNullOrEmpty(text))
			{
				return true;
			}
			Vector3 val = _activePosition + Vector3.up * 0.5f;
			_pendingDamageText = null;
			_pendingDamageTextColor = null;
			if (_activeKind == WorkerBonusKind.Gathering)
			{
				text = LocalizeGatheringBonus(text);
				text = (_pendingDamageText = text.Replace("⚒\ufe0f", "\ud83c\udf3f").Replace("⚒", "\ud83c\udf3f"));
				_pendingDamageTextColor = GatheringColor;
			}
			else if (_activeKind == WorkerBonusKind.Mining)
			{
				_pendingDamageText = text;
				_pendingDamageTextColor = MiningColor;
			}
			if ((Object)(object)DamageText.instance != (Object)null)
			{
				DamageText.instance.ShowText((TextType)7, val, text, true);
			}
			PlayVanillaBonusEffect(_activePosition);
			NotYourLootPlugin.LogInfo("Captain Worker bonus converted to vanilla-style popup: " + $"{_activeKind} / {text}");
			return false;
		}
	}

	[HarmonyPatch(typeof(DamageText), "AddInworldText")]
	private static class DamageTextColorPatch
	{
		private static void Postfix(DamageText __instance, string text)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if (!_pendingDamageTextColor.HasValue || string.IsNullOrEmpty(_pendingDamageText) || !string.Equals(text, _pendingDamageText, StringComparison.Ordinal))
			{
				return;
			}
			try
			{
				if (!(AccessTools.Field(typeof(DamageText), "m_worldTexts")?.GetValue(__instance) is IList { Count: not 0 } list))
				{
					return;
				}
				object obj = list[list.Count - 1];
				if (obj != null)
				{
					object? obj2 = AccessTools.Field(obj.GetType(), "m_textField")?.GetValue(obj);
					TMP_Text val = (TMP_Text)((obj2 is TMP_Text) ? obj2 : null);
					if (!((Object)(object)val == (Object)null))
					{
						Color value = _pendingDamageTextColor.Value;
						value.a = ((Graphic)val).color.a;
						((Graphic)val).color = value;
					}
				}
			}
			finally
			{
				_pendingDamageText = null;
				_pendingDamageTextColor = null;
			}
		}
	}

	[HarmonyPatch]
	private static class CaptainLootSpawnSoundPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type type = AccessTools.TypeByName("CaptainSkillTree.VFX.VFXManager");
			if (type == null)
			{
				yield break;
			}
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				if (declaredMethod.Name == "PlaySound")
				{
					yield return declaredMethod;
				}
			}
		}

		private static bool Prefix(object[] __args)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (_activeKind != WorkerBonusKind.Woodcutting && _activeKind != WorkerBonusKind.Gathering && _activeKind != WorkerBonusKind.Mining)
			{
				return true;
			}
			if (!string.Equals(GetFirstString(__args), "sfx_lootspawn", StringComparison.Ordinal))
			{
				return true;
			}
			if (!_vanillaEffectPlayed)
			{
				PlayVanillaBonusEffect(_activePosition);
			}
			return false;
		}
	}

	[HarmonyPatch]
	private static class CaptainCraftingSoundPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type type = AccessTools.TypeByName("CaptainSkillTree.VFX.VFXManager");
			if (type == null)
			{
				yield break;
			}
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				if (declaredMethod.Name == "PlayVFXAtPosition")
				{
					yield return declaredMethod;
				}
			}
		}

		private static bool Prefix(object[] __args)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (_activeKind != WorkerBonusKind.Crafting)
			{
				return true;
			}
			if (!string.Equals(GetFirstString(__args), "sfx_fader_bell", StringComparison.Ordinal))
			{
				return true;
			}
			Vector3 position = _activePosition;
			for (int i = 0; i < __args.Length; i++)
			{
				if (__args[i] is Vector3 val)
				{
					position = val;
					break;
				}
			}
			PlayVanillaBonusEffect(position);
			NotYourLootPlugin.LogInfo("Captain Crafting bonus sound replaced with vanilla Farming bonus effect.");
			return false;
		}
	}

	private static WorkerBonusKind _activeKind = WorkerBonusKind.None;

	private static Vector3 _activePosition;

	private static bool _vanillaEffectPlayed;

	private static EffectList _vanillaBonusEffect;

	private static bool _searchedForVanillaBonusEffect;

	private static readonly Color GatheringColor = new Color(0.4f, 0.8f, 0.2f, 1f);

	private static readonly Color MiningColor = new Color(0.2f, 0.8f, 0.9f, 1f);

	private static string _pendingDamageText;

	private static Color? _pendingDamageTextColor;

	private static readonly HashSet<string> LocalizedGatheringItems = new HashSet<string>(StringComparer.Ordinal)
	{
		"BH_AslaugsHerb", "BH_Bjorncap", "BH_Boswellia", "BH_Chamomile", "BH_Chicory", "BH_Daisy", "BH_Echinacea", "BH_HelshadeFungus", "BH_Lavender", "BH_LokisTrickcap",
		"BH_NordicFirebloom", "BH_SeidrBlossoms", "BH_SkaldsIvy", "BH_ThorsToadstool", "BH_Valhallaberry", "BH_ValkyrieFern", "BH_VikingsBreadcap", "BFP_Clay", "BFP_Straw"
	};

	private static string LocalizeGatheringBonus(string text)
	{
		if (string.IsNullOrEmpty(text) || LocalizationManager.Instance == null)
		{
			return text;
		}
		return Regex.Replace(text, "\\b(BH|BFP)_([A-Za-z]+)\\b", delegate(Match match)
		{
			string text2 = "$" + match.Groups[1].Value.ToLowerInvariant() + "_" + match.Groups[2].Value.ToLowerInvariant();
			if (!LocalizedGatheringItems.Contains(match.Value))
			{
				return match.Value;
			}
			string text3 = LocalizationManager.Instance.TryTranslate(text2);
			return (!string.IsNullOrEmpty(text3) && !(text3 == text2) && !text3.StartsWith("[")) ? text3 : match.Value;
		});
	}

	private static void PlayVanillaBonusEffect(Vector3 position)
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		if (_vanillaEffectPlayed)
		{
			return;
		}
		_vanillaEffectPlayed = true;
		EffectList vanillaBonusEffect = GetVanillaBonusEffect();
		if (vanillaBonusEffect == null || !vanillaBonusEffect.HasEffects())
		{
			NotYourLootPlugin.LogWarning("Could not locate a vanilla Pickable bonus effect.");
			return;
		}
		ZDOID val = default(ZDOID);
		if ((Object)(object)Player.m_localPlayer != (Object)null)
		{
			val = ((Character)Player.m_localPlayer).GetZDOID();
		}
		vanillaBonusEffect.Create(position, Quaternion.identity, (Transform)null, 1f, -1, val);
	}

	private static EffectList GetVanillaBonusEffect()
	{
		if (_vanillaBonusEffect != null)
		{
			return _vanillaBonusEffect;
		}
		if (_searchedForVanillaBonusEffect)
		{
			return null;
		}
		_searchedForVanillaBonusEffect = true;
		try
		{
			Pickable[] source = Resources.FindObjectsOfTypeAll<Pickable>();
			Pickable val = ((IEnumerable<Pickable>)source).FirstOrDefault((Func<Pickable, bool>)((Pickable p) => (Object)(object)p != (Object)null && p.m_bonusEffect != null && p.m_bonusEffect.HasEffects() && (Object)(object)((Component)p).gameObject != (Object)null && ((Object)((Component)p).gameObject).name.IndexOf("rasp", StringComparison.OrdinalIgnoreCase) >= 0));
			if ((Object)(object)val == (Object)null)
			{
				val = ((IEnumerable<Pickable>)source).FirstOrDefault((Func<Pickable, bool>)((Pickable p) => (Object)(object)p != (Object)null && p.m_bonusEffect != null && p.m_bonusEffect.HasEffects() && (int)p.m_pickRaiseSkill > 0));
			}
			if ((Object)(object)val != (Object)null)
			{
				_vanillaBonusEffect = val.m_bonusEffect;
				NotYourLootPlugin.LogInfo("Vanilla Farming bonus effect sourced from Pickable '" + ((Object)((Component)val).gameObject).name + "'.");
				return _vanillaBonusEffect;
			}
			NotYourLootPlugin.LogWarning("No loaded Pickable with a vanilla bonus effect was found.");
		}
		catch (Exception ex)
		{
			NotYourLootPlugin.LogWarning("Failed locating vanilla Farming bonus effect: " + ex.Message);
		}
		return null;
	}

	private static string GetFirstString(object[] args)
	{
		if (args == null)
		{
			return null;
		}
		for (int i = 0; i < args.Length; i++)
		{
			if (args[i] is string result)
			{
				return result;
			}
		}
		return null;
	}
}