Decompiled source of HornOfCalling v0.1.1

HornOfCalling.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MushroomMods;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using Valheim.SettingsGui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("HornOfCalling")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds a craftable Horn of Calling that sounds a blast when used.")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+0f34766723a6669ef7b79dbcc093f8b56065e786")]
[assembly: AssemblyProduct("Horn of Calling")]
[assembly: AssemblyTitle("HornOfCalling")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.1.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 MushroomMods
{
	internal static class PatchIsolation
	{
		internal static int PatchAllIsolated(Harmony harmony, Assembly assembly, ManualLogSource log)
		{
			int num = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly);
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception ex)
				{
					num++;
					log.LogError((object)("Harmony patch class " + type.FullName + " was skipped and the rest of " + harmony.Id + " is still active. " + ex));
				}
			}
			return num;
		}
	}
}
namespace HornOfCalling
{
	internal static class HornItem
	{
		internal const string PrefabName = "HornOfCalling";

		private const string DisplayName = "Horn of Calling";

		private const string Description = "Sound it, and be heard.";

		private const string CloneSourceItem = "TankardAnniversary";

		private const string StationPrefabName = "piece_workbench";

		private const int MinStationLevel = 1;

		private static readonly (string Item, int Amount, int PerLevel)[] Cost = new(string, int, int)[2]
		{
			("Bronze", 1, 1),
			("DeerHide", 1, 1)
		};

		private static GameObject _prefab;

		private static GameObject _prefabContainer;

		private static Recipe _recipe;

		private static List<Recipe> _recipeList;

		private static bool _stationWarningLogged;

		internal static void EnsureRegistered(ObjectDB odb)
		{
			if ((Object)(object)odb == (Object)null || odb.m_items == null || (Object)(object)odb.GetItemPrefab("Wood") == (Object)null)
			{
				return;
			}
			if ((Object)(object)_prefab == (Object)null)
			{
				_prefab = BuildPrefab(odb);
				if ((Object)(object)_prefab == (Object)null)
				{
					return;
				}
			}
			if (!odb.m_items.Contains(_prefab))
			{
				odb.m_items.Add(_prefab);
				odb.UpdateRegisters();
				Plugin.Log.LogInfo((object)"Registered HornOfCalling with ObjectDB.");
			}
		}

		internal static void EnsureRecipeRegistered(ObjectDB odb)
		{
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Expected O, but got Unknown
			if ((Object)(object)_prefab == (Object)null || (Object)(object)odb == (Object)null || odb.m_recipes == null || (Object)(object)odb.GetItemPrefab("Wood") == (Object)null)
			{
				return;
			}
			ItemDrop component = _prefab.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			foreach (Recipe recipe in odb.m_recipes)
			{
				if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item == (Object)(object)component)
				{
					return;
				}
			}
			CraftingStation val = FindStation();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if ((Object)(object)_recipe != (Object)null)
			{
				_recipeList?.Remove(_recipe);
				Object.Destroy((Object)(object)_recipe);
				_recipe = null;
			}
			_recipeList = null;
			List<Requirement> list = new List<Requirement>();
			(string, int, int)[] cost = Cost;
			for (int i = 0; i < cost.Length; i++)
			{
				(string, int, int) tuple = cost[i];
				string item = tuple.Item1;
				int item2 = tuple.Item2;
				int item3 = tuple.Item3;
				GameObject itemPrefab = odb.GetItemPrefab(item);
				ItemDrop val2 = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					Plugin.Log.LogError((object)("Cannot build the HornOfCalling recipe: no item prefab named " + item + "."));
					return;
				}
				list.Add(new Requirement
				{
					m_resItem = val2,
					m_amount = item2,
					m_amountPerLevel = item3,
					m_recover = true
				});
			}
			_recipe = ScriptableObject.CreateInstance<Recipe>();
			((Object)_recipe).name = "Recipe_HornOfCalling";
			_recipe.m_item = component;
			_recipe.m_amount = 1;
			_recipe.m_enabled = true;
			_recipe.m_craftingStation = val;
			_recipe.m_repairStation = val;
			_recipe.m_minStationLevel = 1;
			_recipe.m_resources = list.ToArray();
			odb.m_recipes.Add(_recipe);
			_recipeList = odb.m_recipes;
			Plugin.Log.LogInfo((object)("Registered the HornOfCalling recipe at piece_workbench (" + odb.m_recipes.Count + " recipes)."));
		}

		internal static void EnsureNetworkRegistered(ZNetScene scene)
		{
			if (!((Object)(object)scene == (Object)null) && !((Object)(object)_prefab == (Object)null))
			{
				RegisterPrefab(scene, _prefab);
				if ((Object)(object)HornSound.EffectPrefab != (Object)null)
				{
					RegisterPrefab(scene, HornSound.EffectPrefab);
				}
			}
		}

		private static void RegisterPrefab(ZNetScene scene, GameObject prefab)
		{
			if (!scene.m_prefabs.Contains(prefab))
			{
				scene.m_prefabs.Add(prefab);
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name);
			if (!scene.m_namedPrefabs.ContainsKey(stableHashCode))
			{
				scene.m_namedPrefabs[stableHashCode] = prefab;
				Plugin.Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ZNetScene."));
			}
		}

		private static CraftingStation FindStation()
		{
			CraftingStation[] array = Resources.FindObjectsOfTypeAll<CraftingStation>();
			if (array == null || array.Length == 0)
			{
				return null;
			}
			CraftingStation[] array2 = array;
			foreach (CraftingStation val in array2)
			{
				if ((Object)(object)val != (Object)null && ((Object)val).name == "piece_workbench")
				{
					return val;
				}
			}
			if (_stationWarningLogged)
			{
				return null;
			}
			_stationWarningLogged = true;
			List<string> list = new List<string>();
			array2 = array;
			foreach (CraftingStation val2 in array2)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					list.Add(((Object)val2).name);
				}
			}
			Plugin.Log.LogWarning((object)("No crafting station prefab named 'piece_workbench'. Found: " + string.Join(", ", list.ToArray())));
			return null;
		}

		private static void StripWeaponStats(SharedData shared)
		{
			//IL_0006: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			shared.m_damages = default(DamageTypes);
			shared.m_damagesPerLevel = default(DamageTypes);
			shared.m_attackForce = 0f;
			shared.m_backstabBonus = 0f;
			shared.m_blockPower = 0f;
			shared.m_blockPowerPerLevel = 0f;
			shared.m_deflectionForce = 0f;
			shared.m_deflectionForcePerLevel = 0f;
			shared.m_timedBlockBonus = 0f;
			shared.m_perfectBlockAdrenaline = 0f;
			shared.m_damageModifiers?.Clear();
			shared.m_skillType = (SkillType)0;
		}

		private static GameObject BuildPrefab(ObjectDB odb)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			GameObject itemPrefab = odb.GetItemPrefab("TankardAnniversary");
			if ((Object)(object)itemPrefab == (Object)null)
			{
				Plugin.Log.LogError((object)"Cannot build HornOfCalling: no item prefab named TankardAnniversary.");
				return null;
			}
			if ((Object)(object)_prefabContainer == (Object)null)
			{
				_prefabContainer = new GameObject("HornOfCallingPrefabs");
				_prefabContainer.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)_prefabContainer);
			}
			GameObject val = Object.Instantiate<GameObject>(itemPrefab, _prefabContainer.transform);
			((Object)val).name = "HornOfCalling";
			ItemDrop component = val.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.Log.LogError((object)"Item prefab TankardAnniversary has no ItemDrop component.");
				Object.Destroy((Object)(object)val);
				return null;
			}
			SharedData shared = component.m_itemData.m_shared;
			shared.m_name = "Horn of Calling";
			shared.m_description = "Sound it, and be heard.";
			shared.m_ammoType = string.Empty;
			StripWeaponStats(shared);
			HornSound.Attach(shared, _prefabContainer.transform);
			Plugin.Log.LogInfo((object)"Built HornOfCalling from TankardAnniversary.");
			return val;
		}
	}
	internal static class HornSound
	{
		private const string ClipResourceName = "HornOfCalling.viking.wav";

		private const string EffectPrefabName = "sfx_hornofcalling";

		private const float MaxDistance = 64f;

		private static readonly (float Metres, float Volume)[] Falloff = new(float, float)[10]
		{
			(0f, 1f),
			(15f, 1f),
			(15.5f, 0.82f),
			(25f, 0.82f),
			(25.5f, 0.64f),
			(35f, 0.64f),
			(35.5f, 0.46f),
			(45f, 0.46f),
			(45.5f, 0.28f),
			(64f, 0.28f)
		};

		private static AudioClip _clip;

		internal static GameObject EffectPrefab { get; private set; }

		internal static bool Attach(SharedData shared, Transform container)
		{
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Expected O, but got Unknown
			if (shared == null)
			{
				return false;
			}
			if ((Object)(object)EffectPrefab == (Object)null)
			{
				GameObject val = FindSfxTemplate(shared);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogError((object)"No ZSFX effect on the clone source to build the horn blast from; the horn will be silent.");
					return false;
				}
				EffectPrefab = Object.Instantiate<GameObject>(val, container);
				((Object)EffectPrefab).name = "sfx_hornofcalling";
				ZSFX component = EffectPrefab.GetComponent<ZSFX>();
				component.m_playOnAwake = true;
				component.m_minDelay = 0f;
				component.m_maxDelay = 0f;
				component.m_minPitch = 1f;
				component.m_maxPitch = 1f;
				component.m_closedCaptionToken = string.Empty;
				component.m_secondaryCaptionToken = string.Empty;
				component.m_hash = StringExtensionMethods.GetStableHashCode("sfx_hornofcalling");
				ShapeFalloff(EffectPrefab.GetComponent<AudioSource>());
				ApplyVolume();
				bool isBatchMode = Application.isBatchMode;
				AudioClip val2 = (isBatchMode ? null : LoadClip());
				component.m_audioClips = (AudioClip[])(object)((!((Object)(object)val2 != (Object)null)) ? new AudioClip[0] : new AudioClip[1] { val2 });
				Plugin.Log.LogInfo((object)("Built the horn blast from " + ((Object)val).name + " (" + (((Object)(object)val2 != (Object)null) ? (val2.length.ToString("0.0") + "s, " + val2.frequency + " Hz") : (isBatchMode ? "no audio decoded - dedicated server" : "no audio - the blast will be silent on this peer")) + "), audible to " + 64f.ToString("0") + " m."));
			}
			shared.m_startEffect.m_effectPrefabs = (EffectData[])(object)new EffectData[1]
			{
				new EffectData
				{
					m_prefab = EffectPrefab,
					m_enabled = true,
					m_variant = -1,
					m_attach = true,
					m_inheritParentRotation = true
				}
			};
			return true;
		}

		internal static void SetVolume(float volume)
		{
			ZSFX val = (((Object)(object)EffectPrefab != (Object)null) ? EffectPrefab.GetComponent<ZSFX>() : null);
			if (!((Object)(object)val == (Object)null))
			{
				val.m_minVol = (val.m_maxVol = Mathf.Clamp01(volume));
			}
		}

		internal static void ApplyVolume()
		{
			SetVolume(Plugin.BlastVolume.Value);
		}

		internal static bool PrepareToPreview(AudioSource source)
		{
			if ((Object)(object)source == (Object)null)
			{
				return false;
			}
			AudioSource val = (((Object)(object)EffectPrefab != (Object)null) ? EffectPrefab.GetComponent<AudioSource>() : null);
			AudioClip val2 = (((Object)(object)val != (Object)null) ? LoadClip() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			source.clip = val2;
			source.outputAudioMixerGroup = val.outputAudioMixerGroup;
			source.playOnAwake = false;
			source.loop = false;
			source.spatialBlend = 0f;
			source.bypassReverbZones = true;
			return true;
		}

		private static void ShapeFalloff(AudioSource source)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			if ((Object)(object)source == (Object)null)
			{
				Plugin.Log.LogWarning((object)"The horn blast has no AudioSource; its range is whatever it was cloned with.");
				return;
			}
			source.rolloffMode = (AudioRolloffMode)2;
			source.maxDistance = 64f;
			Keyframe[] array = (Keyframe[])(object)new Keyframe[Falloff.Length];
			for (int i = 0; i < Falloff.Length; i++)
			{
				array[i] = new Keyframe(Falloff[i].Metres / 64f, Falloff[i].Volume, 0f, 0f);
			}
			source.SetCustomCurve((AudioSourceCurveType)0, new AnimationCurve(array));
		}

		private static GameObject FindSfxTemplate(SharedData shared)
		{
			EffectData[] array = shared.m_startEffect?.m_effectPrefabs;
			if (array == null)
			{
				return null;
			}
			EffectData[] array2 = array;
			foreach (EffectData val in array2)
			{
				if ((Object)(object)val?.m_prefab != (Object)null && (Object)(object)val.m_prefab.GetComponent<ZSFX>() != (Object)null)
				{
					return val.m_prefab;
				}
			}
			return null;
		}

		private static AudioClip LoadClip()
		{
			if ((Object)(object)_clip != (Object)null)
			{
				return _clip;
			}
			byte[] array = ReadResource();
			if (array == null)
			{
				return null;
			}
			try
			{
				_clip = Decode(array, "hornofcalling_blast");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not decode HornOfCalling.viking.wav: " + ex));
			}
			return _clip;
		}

		private static byte[] ReadResource()
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			using Stream stream = executingAssembly.GetManifestResourceStream("HornOfCalling.viking.wav");
			if (stream == null)
			{
				Plugin.Log.LogError((object)("Embedded resource HornOfCalling.viking.wav is missing. Found: " + string.Join(", ", executingAssembly.GetManifestResourceNames())));
				return null;
			}
			byte[] array = new byte[stream.Length];
			int num;
			for (int i = 0; i < array.Length; i += num)
			{
				num = stream.Read(array, i, array.Length - i);
				if (num <= 0)
				{
					break;
				}
			}
			return array;
		}

		private static AudioClip Decode(byte[] wav, string clipName)
		{
			if (wav.Length < 12 || Tag(wav, 0) != "RIFF" || Tag(wav, 8) != "WAVE")
			{
				Plugin.Log.LogError((object)"Audio resource is not a RIFF/WAVE file.");
				return null;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = -1;
			int num6 = 0;
			int num7 = 12;
			while (num7 + 8 <= wav.Length)
			{
				string text = Tag(wav, num7);
				int num8 = BitConverter.ToInt32(wav, num7 + 4);
				int num9 = num7 + 8;
				if (num8 < 0 || num9 + num8 > wav.Length)
				{
					num8 = wav.Length - num9;
				}
				if (text == "fmt " && num8 >= 16)
				{
					num = BitConverter.ToInt16(wav, num9);
					num2 = BitConverter.ToInt16(wav, num9 + 2);
					num3 = BitConverter.ToInt32(wav, num9 + 4);
					num4 = BitConverter.ToInt16(wav, num9 + 14);
				}
				else if (text == "data")
				{
					num5 = num9;
					num6 = num8;
				}
				num7 = num9 + num8 + (num8 & 1);
			}
			if (num5 < 0 || num2 <= 0 || num3 <= 0)
			{
				Plugin.Log.LogError((object)"Audio resource has no usable fmt/data chunks.");
				return null;
			}
			if (num != 1 || num4 != 16)
			{
				Plugin.Log.LogError((object)("Audio resource must be 16-bit PCM; got format " + num + " at " + num4 + " bits."));
				return null;
			}
			int num10 = num6 / 2;
			int num11 = num10 / num2;
			if (num11 <= 0)
			{
				Plugin.Log.LogError((object)"Audio resource holds no samples.");
				return null;
			}
			float[] array = new float[num10];
			for (int i = 0; i < num10; i++)
			{
				int num12 = num5 + i * 2;
				array[i] = (float)(short)(wav[num12] | (wav[num12 + 1] << 8)) / 32768f;
			}
			AudioClip obj = AudioClip.Create(clipName, num11, num2, num3, false);
			obj.SetData(array, 0);
			return obj;
		}

		private static string Tag(byte[] bytes, int at)
		{
			return Encoding.ASCII.GetString(bytes, at, 4);
		}
	}
	[HarmonyPatch(typeof(ObjectDB))]
	internal static class ObjectDbPatches
	{
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		[HarmonyPatch("Awake")]
		internal static void AwakePostfix(ObjectDB __instance)
		{
			try
			{
				HornItem.EnsureRegistered(__instance);
				HornItem.EnsureRecipeRegistered(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("ObjectDB.Awake registration failed: " + ex));
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(800)]
		[HarmonyPatch("CopyOtherDB")]
		internal static void CopyOtherDbPostfix(ObjectDB __instance)
		{
			try
			{
				HornItem.EnsureRegistered(__instance);
				HornItem.EnsureRecipeRegistered(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("ObjectDB.CopyOtherDB registration failed: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateKnownRecipesList")]
	internal static class UpdateKnownRecipesListPatch
	{
		[HarmonyPrefix]
		internal static void Prefix()
		{
			try
			{
				HornItem.EnsureRecipeRegistered(ObjectDB.instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Recipe re-registration failed: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetSceneAwakePatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		internal static void Postfix(ZNetScene __instance)
		{
			try
			{
				HornItem.EnsureRegistered(ObjectDB.instance);
				HornItem.EnsureRecipeRegistered(ObjectDB.instance);
				HornItem.EnsureNetworkRegistered(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to register HornOfCalling with ZNetScene: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class GameStartPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		internal static void Postfix()
		{
			try
			{
				HornItem.EnsureRegistered(ObjectDB.instance);
				HornItem.EnsureRecipeRegistered(ObjectDB.instance);
				HornItem.EnsureNetworkRegistered(ZNetScene.instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Game.Start registration failed: " + ex));
			}
		}
	}
	[BepInPlugin("com.greg.hornofcalling", "HornOfCalling", "0.1.1")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	public class Plugin : BaseUnityPlugin
	{
		internal const string Guid = "com.greg.hornofcalling";

		internal const string Name = "HornOfCalling";

		internal const string Version = "0.1.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> BlastVolume;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BlastVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "BlastVolume", 1f, new ConfigDescription("How loud the horn blast is, as a fraction of the recorded level. This multiplies Valheim's own sound-effects volume rather than replacing it, and applies to every blast you hear, whoever sounded it. Also on the Audio tab of the in-game settings menu, which writes back here.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			BlastVolume.SettingChanged += delegate
			{
				HornSound.ApplyVolume();
			};
			_harmony = new Harmony("com.greg.hornofcalling");
			int num = PatchIsolation.PatchAllIsolated(_harmony, typeof(Plugin).Assembly, Log);
			Log.LogInfo((object)("HornOfCalling 0.1.1 loaded." + ((num > 0) ? (" " + num + " patch class(es) skipped - see the errors above.") : "")));
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class VolumeSlider
	{
		private const string RowName = "HornOfCallingVolume";

		private const string Label = "Horn of Calling";

		private static Slider _slider;

		private static TMP_Text _value;

		private static VolumePreview _preview;

		internal static void Build(AudioSettings tab)
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			Slider sfxVolumeSlider = tab.m_sfxVolumeSlider;
			TMP_Text sfxVolumeText = tab.m_sfxVolumeText;
			if ((Object)(object)sfxVolumeSlider == (Object)null || (Object)(object)sfxVolumeText == (Object)null)
			{
				Plugin.Log.LogWarning((object)"The Audio settings tab has no SFX volume row to clone; the horn volume slider will not appear. The setting is still editable in the config file.");
			}
			else if (!((Object)(object)((Component)sfxVolumeSlider).transform.parent.Find("HornOfCallingVolume") != (Object)null))
			{
				GameObject obj = Object.Instantiate<GameObject>(((Component)sfxVolumeSlider).gameObject, ((Component)sfxVolumeSlider).transform.parent);
				((Object)obj).name = "HornOfCallingVolume";
				Transform val = (((Object)(object)tab.m_musicVolumeSlider != (Object)null) ? ((Component)tab.m_musicVolumeSlider).transform : ((Component)sfxVolumeSlider).transform);
				obj.transform.SetSiblingIndex(val.GetSiblingIndex() + 1);
				_slider = obj.GetComponent<Slider>();
				_value = FindValueText(obj.transform, ((Object)sfxVolumeText).name);
				SetLabel(obj.transform, _value);
				_slider.onValueChanged = new SliderEvent();
				_slider.minValue = 0f;
				_slider.maxValue = 1f;
				_slider.wholeNumbers = false;
				_slider.value = Mathf.Clamp01(Plugin.BlastVolume.Value);
				((UnityEvent<float>)(object)_slider.onValueChanged).AddListener((UnityAction<float>)OnSliderChanged);
				_preview = obj.AddComponent<VolumePreview>();
				_preview.Initialise();
				SpliceIntoNavigation(((Component)val).GetComponent<Selectable>(), (Selectable)(object)_slider);
				UpdateValueText();
				Plugin.Log.LogDebug((object)"Added the horn volume row to the Audio settings tab.");
			}
		}

		internal static void Commit()
		{
			if (!((Object)(object)_slider == (Object)null))
			{
				Plugin.BlastVolume.Value = Mathf.Clamp01(_slider.value);
				Forget();
			}
		}

		internal static void Revert()
		{
			HornSound.ApplyVolume();
			Forget();
		}

		private static void Forget()
		{
			_slider = null;
			_value = null;
			_preview = null;
		}

		private static void OnSliderChanged(float value)
		{
			HornSound.SetVolume(value);
			UpdateValueText();
			if ((Object)(object)_preview != (Object)null)
			{
				_preview.Schedule(value);
			}
		}

		private static void UpdateValueText()
		{
			if (!((Object)(object)_value == (Object)null) && !((Object)(object)_slider == (Object)null))
			{
				_value.text = Mathf.Round(_slider.value * 100f) + "%";
			}
		}

		private static TMP_Text FindValueText(Transform row, string valueName)
		{
			Transform val = row.Find(valueName);
			TMP_Text obj = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TMP_Text>() : null);
			if ((Object)(object)obj == (Object)null)
			{
				Plugin.Log.LogWarning((object)("No '" + valueName + "' text under the cloned volume row; the horn slider will show the SFX percentage instead of its own."));
			}
			return obj;
		}

		private static void SetLabel(Transform row, TMP_Text value)
		{
			List<TMP_Text> list = new List<TMP_Text>(((Component)row).GetComponentsInChildren<TMP_Text>(true));
			foreach (TMP_Text item in list)
			{
				if (!((Object)(object)item == (Object)(object)value))
				{
					item.text = "Horn of Calling";
					return;
				}
			}
			Plugin.Log.LogWarning((object)("No caption text under the cloned volume row; the horn slider will be labelled as the SFX one. Texts found: " + list.Count + "."));
		}

		private static void SpliceIntoNavigation(Selectable previous, Selectable inserted)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)previous == (Object)null) && !((Object)(object)inserted == (Object)null))
			{
				Navigation navigation = previous.navigation;
				Selectable selectOnDown = ((Navigation)(ref navigation)).selectOnDown;
				GuiUtils.SetNavigationDown(previous, inserted);
				GuiUtils.SetNavigationUp(inserted, previous);
				GuiUtils.SetNavigationDown(inserted, selectOnDown);
				if ((Object)(object)selectOnDown != (Object)null)
				{
					GuiUtils.SetNavigationUp(selectOnDown, inserted);
				}
			}
		}
	}
	internal class VolumePreview : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IPointerUpHandler
	{
		private const float SettleSeconds = 0.35f;

		private AudioSource _source;

		private float _dueAt = -1f;

		private float _level = 1f;

		private bool _held;

		internal void Initialise()
		{
			_source = ((Component)this).gameObject.AddComponent<AudioSource>();
			if (!HornSound.PrepareToPreview(_source))
			{
				Object.Destroy((Object)(object)_source);
				_source = null;
				Plugin.Log.LogDebug((object)"No horn blast to preview; the volume slider will be silent.");
			}
		}

		internal void Schedule(float level)
		{
			if (!((Object)(object)_source == (Object)null))
			{
				_level = level;
				_dueAt = Time.unscaledTime + 0.35f;
			}
		}

		public void OnPointerDown(PointerEventData eventData)
		{
			_held = true;
		}

		public void OnPointerUp(PointerEventData eventData)
		{
			_held = false;
		}

		private void Update()
		{
			if (!(_dueAt < 0f) && !_held && !(Time.unscaledTime < _dueAt))
			{
				_dueAt = -1f;
				_source.Stop();
				_source.volume = Mathf.Clamp01(_level);
				_source.Play();
			}
		}
	}
	[HarmonyPatch(typeof(AudioSettings))]
	internal static class AudioSettingsPatches
	{
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		[HarmonyPatch("Initialize")]
		internal static void InitializePostfix(AudioSettings __instance)
		{
			try
			{
				VolumeSlider.Build(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not add the horn volume slider: " + ex));
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("OnOkAsync")]
		internal static void OnOkAsyncPostfix()
		{
			try
			{
				VolumeSlider.Commit();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not save the horn volume: " + ex));
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("OnBack")]
		internal static void OnBackPostfix()
		{
			try
			{
				VolumeSlider.Revert();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not restore the horn volume: " + ex));
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}