Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HornOfCalling v0.1.1
HornOfCalling.dll
Decompiled 2 weeks agousing 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) { } } }