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 Epic Loot Addons v1.0.2
DigiEpicLoot.dll
Decompiled 19 hours agousing 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.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DigiCore; using EnchantGlowToggle; using EpicLoot; using EpicLoot.MagicItemEffects; using EpicLootFixes; using EpicLootStorageMaterials; using EpicLoot_UnityLib; using HarmonyLib; using Microsoft.CodeAnalysis; using ShardExclusivity; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("DigiEpicLoot")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("DigiEpicLoot")] [assembly: AssemblyTitle("DigiEpicLoot")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal static class SettingOrder { internal sealed class ConfigurationManagerAttributes { public int? Order; } internal static void Apply(ConfigFile config) { //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown FieldInfo fieldInfo = AccessTools.Field(typeof(ConfigEntryBase), "<Description>k__BackingField"); if (fieldInfo == null) { return; } int num = 1000; foreach (ConfigDefinition item in config.Keys.ToList()) { ConfigEntryBase val = config[item]; ConfigDescription description = val.Description; object[] array = ((description != null) ? description.Tags : null) ?? new object[0]; object obj = array.FirstOrDefault((object t) => t != null && t.GetType().Name == "ConfigurationManagerAttributes"); if (obj != null) { FieldInfo fieldInfo2 = AccessTools.Field(obj.GetType(), "Order"); if (fieldInfo2 != null && fieldInfo2.GetValue(obj) == null) { fieldInfo2.SetValue(obj, num); } } else { fieldInfo.SetValue(val, (object?)new ConfigDescription(((description != null) ? description.Description : null) ?? "", (description != null) ? description.AcceptableValues : null, array.Concat(new object[1] { new ConfigurationManagerAttributes { Order = num } }).ToArray())); } num--; } } } namespace DigiCore { public sealed class ModuleInfo { public readonly Type Type; public readonly string Title; public readonly string OldGuid; public readonly string Description; public readonly string[] Requires; public readonly bool DefaultOn; public ModuleInfo(Type type, string title, string oldGuid, string description, string[] requires = null, bool defaultOn = true) { Type = type; Title = title; OldGuid = oldGuid; Description = description; Requires = requires ?? new string[0]; DefaultOn = defaultOn; } } public abstract class DigiPlugin : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func<string, string> <>9__11_0; public static Func<string, bool> <>9__11_1; public static Func<string, bool> <>9__11_2; public static Func<string, string> <>9__11_3; public static Func<string, string[]> <>9__18_0; public static Func<string[], string> <>9__18_1; public static Func<string[], string> <>9__18_4; public static Func<IGrouping<string, string[]>, string> <>9__18_2; public static PopupButtonCallback <>9__18_3; public static Func<string, bool> <>9__22_0; internal string <Awake>b__11_0(string l) { return l.Trim(); } internal bool <Awake>b__11_1(string l) { return l.Length > 0; } internal bool <Awake>b__11_2(string r) { return !Chainloader.PluginInfos.ContainsKey(r); } internal string <Awake>b__11_3(string r) { if (!RequirementNames.TryGetValue(r, out var value)) { return r; } return value; } internal string[] <ShowDuplicates>b__18_0(string d) { return d.Split(new char[1] { '|' }); } internal string <ShowDuplicates>b__18_1(string[] d) { return d[0]; } internal string <ShowDuplicates>b__18_2(IGrouping<string, string[]> g) { return "<b>" + g.Key + ":</b> " + string.Join(", ", g.Select((string[] d) => d[1])); } internal string <ShowDuplicates>b__18_4(string[] d) { return d[1]; } internal void <ShowDuplicates>b__18_3() { UnifiedPopup.Pop(); } internal bool <Apply>b__22_0(string r) { return !Chainloader.PluginInfos.ContainsKey(r); } } public const string ModulesSection = "0 - Sub-mods"; internal static DigiPlugin StartingHost; internal static ModuleInfo StartingModule; private static readonly Dictionary<string, string> RequirementNames = new Dictionary<string, string> { { "randyknapp.mods.epicloot", "Epic Loot" }, { "com.jotunn.jotunn", "Jotunn" }, { "ValheimLegends", "Valheim Legends" }, { "vapok.mods.adventurebackpacks", "Adventure Backpacks" }, { "MidnightsFX.StarLevelSystem", "Star Level System" }, { "sosal.nordicrugs", "Nordic Rugs" }, { "LuminairPrime.valheim.localcraftfromstorageprime", "Local Craft From Storage Prime" } }; private readonly Dictionary<ModuleInfo, DigiModule> _running = new Dictionary<ModuleInfo, DigiModule>(); private readonly Dictionary<ModuleInfo, ConfigEntry<bool>> _switches = new Dictionary<ModuleInfo, ConfigEntry<bool>>(); private HashSet<string> _migrated; private const string DuplicatesKey = "digi.valheim.duplicates"; private const string DuplicatesShownKey = "digi.valheim.duplicates.shown"; protected abstract ModuleInfo[] Modules { get; } private string MigratedFile => Path.Combine(Paths.ConfigPath, ((BaseUnityPlugin)this).Info.Metadata.GUID + ".migrated.txt"); private static List<string> Duplicates { get { List<string> list = AppDomain.CurrentDomain.GetData("digi.valheim.duplicates") as List<string>; if (list == null) { list = new List<string>(); AppDomain.CurrentDomain.SetData("digi.valheim.duplicates", list); } return list; } } protected virtual void Awake() { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; ((BaseUnityPlugin)this).Config.Save(); _migrated = (File.Exists(MigratedFile) ? new HashSet<string>(from l in File.ReadAllLines(MigratedFile) select l.Trim() into l where l.Length > 0 select l) : new HashSet<string>()); bool flag = !_migrated.Contains("#switches"); ModuleInfo[] modules = Modules; foreach (ModuleInfo moduleInfo in modules) { List<string> list = moduleInfo.Requires.Where((string r) => !Chainloader.PluginInfos.ContainsKey(r)).ToList(); string value; string text = ((moduleInfo.Requires.Length == 0) ? "" : (" Needs " + string.Join(" and ", moduleInfo.Requires.Select((string r) => (!RequirementNames.TryGetValue(r, out value)) ? r : value)) + ".")); ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Sub-mods", moduleInfo.Title, moduleInfo.DefaultOn, moduleInfo.Description + text); if (flag && !moduleInfo.DefaultOn && File.Exists(OldConfigPath(moduleInfo))) { val.Value = true; } _switches[moduleInfo] = val; ModuleInfo captured = moduleInfo; val.SettingChanged += delegate { Apply(captured); }; if (list.Count > 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)(moduleInfo.Title + ": off, needs " + string.Join(", ", list) + ".")); } } if (flag) { Remember("#switches"); } modules = Modules; foreach (ModuleInfo module in modules) { Apply(module); } } private void Start() { foreach (KeyValuePair<ModuleInfo, DigiModule> item in _running.ToList()) { if (!((Object)(object)item.Value == (Object)null) && Chainloader.PluginInfos.ContainsKey(item.Key.OldGuid)) { item.Value.Teardown(); _running.Remove(item.Key); Forget(item.Key.OldGuid); ReportDuplicate(item.Key); } } SettingOrder.Apply(((BaseUnityPlugin)this).Config); if (Duplicates.Count > 0 && !Application.isBatchMode) { ((MonoBehaviour)this).StartCoroutine(ShowDuplicates()); } } private void ReportDuplicate(ModuleInfo module) { ((BaseUnityPlugin)this).Logger.LogWarning((object)(module.Title + ": not started, because the separate mod (" + module.OldGuid + ") is installed too. Remove that one.")); string item = ((BaseUnityPlugin)this).Info.Metadata.Name.Replace("Digi's ", "") + "|" + module.Title; if (!Duplicates.Contains(item)) { Duplicates.Add(item); } } private IEnumerator ShowDuplicates() { while ((Object)(object)FejdStartup.instance == (Object)null || !UnifiedPopup.IsAvailable()) { yield return (object)new WaitForSeconds(0.5f); } yield return (object)new WaitForSeconds(1f); if (AppDomain.CurrentDomain.GetData("digi.valheim.duplicates.shown") != null) { yield break; } AppDomain.CurrentDomain.SetData("digi.valheim.duplicates.shown", true); IEnumerable<string> values = from d in Duplicates select d.Split(new char[1] { '|' }) into d group d by d[0] into g select "<b>" + g.Key + ":</b> " + string.Join(", ", g.Select((string[] d) => d[1])); string text = "These mods are installed twice: on their own and inside one of our combined mods.\n\n" + string.Join("\n", values) + "\n\nThe copy in the combined mod stays off until you remove the separate ones in your mod manager (or make a new profile with Digi Modpack). Your settings are copied over."; object obj = <>c.<>9__18_3; if (obj == null) { PopupButtonCallback val = delegate { UnifiedPopup.Pop(); }; <>c.<>9__18_3 = val; obj = (object)val; } UnifiedPopup.Push((PopupBase)new WarningPopup("DigiTeam mods installed twice", text, (PopupButtonCallback)obj, false)); } private static string OldConfigPath(ModuleInfo module) { return Path.Combine(Paths.ConfigPath, module.OldGuid + ".cfg"); } private void Remember(string key) { if (!_migrated.Add(key)) { return; } try { File.AppendAllText(MigratedFile, key + Environment.NewLine); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not write " + MigratedFile + ": " + ex.Message)); } } private void Forget(string key) { if (!_migrated.Remove(key)) { return; } try { File.WriteAllLines(MigratedFile, _migrated.ToArray()); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not write " + MigratedFile + ": " + ex.Message)); } } private void Apply(ModuleInfo module) { bool value = _switches[module].Value; _running.TryGetValue(module, out var value2); if (value && (Object)(object)value2 == (Object)null) { if (!module.Requires.Any((string r) => !Chainloader.PluginInfos.ContainsKey(r))) { if (Chainloader.PluginInfos.ContainsKey(module.OldGuid)) { ReportDuplicate(module); } else { StartModule(module); } } } else if (!value && (Object)(object)value2 != (Object)null) { value2.Teardown(); _running.Remove(module); ((BaseUnityPlugin)this).Logger.LogInfo((object)(module.Title + ": switched off.")); } } private void StartModule(ModuleInfo module) { StartingHost = this; StartingModule = module; try { DigiModule digiModule = (DigiModule)(object)((Component)this).gameObject.AddComponent(module.Type); digiModule.Host = this; digiModule.Info2 = module; _running[module] = digiModule; MigrateOldSettings(module, digiModule); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"{module.Title} could not start: {arg}"); } finally { StartingHost = null; StartingModule = null; } SettingOrder.Apply(((BaseUnityPlugin)this).Config); } private void MigrateOldSettings(ModuleInfo module, DigiModule component) { if (_migrated.Contains(module.OldGuid)) { return; } string path = OldConfigPath(module); if (File.Exists(path) && component.ConfigOrNull != null) { Dictionary<string, string> dictionary = ReadCfg(path); int num = 0; foreach (ModuleConfig.Bound entry in component.ConfigOrNull.Entries) { if (dictionary.TryGetValue(entry.Section + "\n" + entry.Key, out var value)) { try { entry.Entry.SetSerializedValue(value); num++; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)(module.Title + ": could not copy '" + entry.Key + "': " + ex.Message)); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"{module.Title}: copied {num} setting(s) from {Path.GetFileName(path)}."); } Remember(module.OldGuid); } private static Dictionary<string, string> ReadCfg(string path) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); string text = ""; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#")) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2.Substring(1, text2.Length - 2); continue; } int num = text2.IndexOf(" = ", StringComparison.Ordinal); if (num > 0) { dictionary[text + "\n" + text2.Substring(0, num).Trim()] = text2.Substring(num + 3); } } return dictionary; } protected virtual void OnDestroy() { foreach (DigiModule item in _running.Values.ToList()) { item?.Teardown(); } _running.Clear(); } public bool IsRunning(Type moduleType) { return _running.Any((KeyValuePair<ModuleInfo, DigiModule> p) => p.Key.Type == moduleType && (Object)(object)p.Value != (Object)null); } } public abstract class DigiModule : MonoBehaviour { internal DigiPlugin Host; internal ModuleInfo Info2; private ModuleConfig _config; private ManualLogSource _logger; private Harmony _harmony; internal ModuleConfig ConfigOrNull => _config; protected ModuleConfig Config => _config ?? (_config = new ModuleConfig(((BaseUnityPlugin)(Host ?? DigiPlugin.StartingHost)).Config, (Info2 ?? DigiPlugin.StartingModule).Title)); protected ManualLogSource Logger => _logger ?? (_logger = Logger.CreateLogSource("Digi's " + (Info2 ?? DigiPlugin.StartingModule)?.Title)); protected Harmony ModuleHarmony { get { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_004a: Expected O, but got Unknown Harmony obj = _harmony; if (obj == null) { Harmony val = new Harmony(((object)this).GetType().Namespace?.ToLowerInvariant() + "." + Guid.NewGuid().ToString("N")); Harmony val2 = val; _harmony = val; obj = val2; } return obj; } } protected Harmony PatchModule(Type type) { ModuleHarmony.PatchAll(type); return ModuleHarmony; } protected Harmony PatchModuleNamespace() { string text = ((object)this).GetType().Namespace; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(((object)this).GetType().Assembly); foreach (Type type in typesFromAssembly) { if (type.Namespace == text || (type.Namespace != null && text != null && type.Namespace.StartsWith(text + "."))) { ModuleHarmony.CreateClassProcessor(type).Patch(); } } return ModuleHarmony; } internal void Teardown() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { Logger.LogWarning((object)("Unpatch: " + ex.Message)); } Object.Destroy((Object)(object)this); } } public sealed class ModuleConfig { public sealed class Bound { public string Section; public string Key; public ConfigEntryBase Entry; } private readonly ConfigFile _file; private readonly string _prefix; internal readonly List<Bound> Entries = new List<Bound>(); public ConfigFile File => _file; public string ConfigFilePath => _file.ConfigFilePath; internal ModuleConfig(ConfigFile file, string title) { _file = file; _prefix = title + " - "; } public void Reload() { } public void Save() { _file.Save(); } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription configDescription = null) { ConfigEntry<T> val = _file.Bind<T>(_prefix + section, key, defaultValue, configDescription); if (!Entries.Any((Bound b) => b.Section == section && b.Key == key)) { Entries.Add(new Bound { Section = section, Key = key, Entry = (ConfigEntryBase)(object)val }); } return val; } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown return Bind(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } public ConfigEntry<T> Bind<T>(ConfigDefinition definition, T defaultValue, ConfigDescription configDescription = null) { return Bind(definition.Section, definition.Key, defaultValue, configDescription); } public bool TryGetEntry<T>(ConfigDefinition definition, out ConfigEntry<T> entry) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown return _file.TryGetEntry<T>(new ConfigDefinition(_prefix + definition.Section, definition.Key), ref entry); } } } namespace DigiEpicLoot { [BepInPlugin("digi.valheim.epiclootaddons", "Digi's Epic Loot Add-ons", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [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 Plugin : DigiPlugin { public const string Guid = "digi.valheim.epiclootaddons"; public const string Name = "Digi's Epic Loot Add-ons"; public const string Version = "1.0.2"; protected override ModuleInfo[] Modules { get; } = new ModuleInfo[4] { new ModuleInfo(typeof(EpicLootStorageMaterials.Plugin), "Epic Loot Storage Materials", "digi.valheim.epiclootstoragematerials", "The enchanting table uses materials, trophies and shardstones from nearby chests.", new string[1] { "randyknapp.mods.epicloot" }), new ModuleInfo(typeof(EpicLootFixes.Plugin), "Epic Loot Fixes", "digi.valheim.epiclootfixes", "Eitr cost on enchanted staffs, a Lucky Loot message, and Lucky Loot on birds.", new string[1] { "randyknapp.mods.epicloot" }), new ModuleInfo(typeof(EnchantGlowToggle.Plugin), "Enchant Glow Toggle", "digi.valheim.enchantglowtoggle", "Hide or show enchantment glow and legendary looks, synced with friends.", new string[1] { "randyknapp.mods.epicloot" }), new ModuleInfo(typeof(ShardExclusivity.Plugin), "Shard Exclusivity", "digi.valheim.shardexclusivity", "Shardstones can be socketed even when their effect normally clashes.", new string[1] { "randyknapp.mods.epicloot" }) }; } } namespace ShardExclusivity { public class Plugin : DigiModule { public const string Guid = "digi.valheim.shardexclusivity"; public const string Name = "Digi's Shard Exclusivity"; public const string Version = "1.0.2"; private const string EpicLootGuid = "randyknapp.mods.epicloot"; private static ConfigEntry<bool> _enabled; private static MethodInfo _resolve; private static FieldInfo _exclusive; private static object _runeColor; private static int _shardDepth; private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.shardexclusivity".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { MigrateConfig(); _enabled = base.Config.Bind("General", "Ignore effect exclusions for shards", defaultValue: true, "Shardstones can be put into an item even when their effect normally clashes with an effect the item already has. Enchanting and loot keep the normal rules."); Type type = AccessTools.TypeByName("EpicLoot.ShardStones.ShardSocketManager"); Type type2 = AccessTools.TypeByName("EpicLoot.MagicItemEffectRequirements"); Type type3 = AccessTools.TypeByName("EpicLoot.ShardStones.ShardType") ?? AccessTools.TypeByName("EpicLoot.ShardType"); _resolve = ((type == null) ? null : AccessTools.Method(type, "ResolveSocketedEffect", (Type[])null, (Type[])null)); _exclusive = ((type2 == null) ? null : AccessTools.Field(type2, "ExclusiveEffectTypes")); _runeColor = ((type3 == null) ? null : Enum.Parse(type3, "None")); MethodInfo methodInfo = ((type2 == null) ? null : AccessTools.Method(type2, "CheckRequirements", new Type[9] { typeof(ItemData), AccessTools.TypeByName("EpicLoot.MagicItem"), AccessTools.TypeByName("EpicLoot.RequirementFailure").MakeByRefType(), typeof(string).MakeByRefType(), typeof(string), typeof(bool), typeof(bool), typeof(bool), typeof(bool) }, (Type[])null)); if (_resolve == null || _exclusive == null || _runeColor == null || methodInfo == null) { base.Logger.LogError((object)"Disabled, Epic Loot's shard socket internals were not found."); return; } Harmony moduleHarmony = base.ModuleHarmony; string[] array = new string[3] { "CanSocket", "CanCoexist", "AddShard" }; foreach (string text in array) { MethodInfo methodInfo2 = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (!(methodInfo2 == null)) { moduleHarmony.Patch((MethodBase)methodInfo2, Hook("SocketPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, Hook("SocketFinalizer"), (HarmonyMethod)null); } } moduleHarmony.Patch((MethodBase)methodInfo, Hook("CheckPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, Hook("CheckFinalizer"), (HarmonyMethod)null); base.Logger.LogInfo((object)"Shardstones ignore effect exclusions when socketed."); } private static HarmonyMethod Hook(string name) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return new HarmonyMethod(typeof(Plugin).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic)); } private static void SocketPrefix(ItemData equipment, ItemData input, out bool __state) { __state = false; if (!_enabled.Value || equipment == null || input == null) { return; } object[] array = new object[5] { equipment, input, null, null, null }; try { if (!(bool)_resolve.Invoke(null, array) || object.Equals(array[3], _runeColor)) { return; } } catch { return; } _shardDepth++; __state = true; } private static Exception SocketFinalizer(Exception __exception, bool __state) { if (__state) { _shardDepth--; } return __exception; } private static void CheckPrefix(object __instance, out List<string> __state) { __state = null; if (_shardDepth > 0 && _exclusive.GetValue(__instance) is List<string> { Count: not 0 } list) { __state = list; _exclusive.SetValue(__instance, new List<string>()); } } private static Exception CheckFinalizer(Exception __exception, object __instance, List<string> __state) { if (__state != null) { _exclusive.SetValue(__instance, __state); } return __exception; } } } namespace EpicLootStorageMaterials { public class Plugin : DigiModule { [HarmonyPatch(typeof(Container), "Awake")] private static class ContainerRegistry { private static void Postfix(Container __instance) { Containers.Add(__instance); } } [HarmonyPatch(typeof(SacrificeUI), "SacrificeItems")] private static class SacrificePatch { private static void Prefix() { Sacrificed.Clear(); if (_perItemDouble.Value && !(ProductsMethod == null) && !(GiveMethod == null)) { EnchantingTableUI instance = EnchantingTableUI.instance; EnchantingTable val = ((instance != null) ? instance.SourceTable : null); if (!((Object)(object)val == (Object)null)) { Tuple<float, float> featureCurrentValue = val.GetFeatureCurrentValue((EnchantingFeature)0); _doubleChance = (float.IsNaN(featureCurrentValue.Item1) ? 0f : (featureCurrentValue.Item1 / 100f)); _sacrificing = true; } } } private static void Postfix(SacrificeUI __instance) { if (!_sacrificing) { return; } _sacrificing = false; List<Tuple<ItemData, int>> list = new List<Tuple<ItemData, int>>(); foreach (Tuple<ItemData, int> item in Sacrificed) { int num = 0; for (int i = 0; i < item.Item2; i++) { if (Random.value < _doubleChance) { num++; } } if (num > 0) { list.Add(Tuple.Create<ItemData, int>(item.Item1, num)); } } if (list.Count != 0) { object obj = ProductsMethod.Invoke(null, new object[1] { list }); EnchantingTableUI instance = EnchantingTableUI.instance; if (instance != null) { instance.PlayEnchantBonusSFX(); } if ((Object)(object)__instance.BonusPanel != (Object)null) { __instance.BonusPanel.Show(); } GiveMethod.Invoke(null, new object[1] { obj }); RefreshSacrifice?.Invoke(__instance, null); } } private static Exception Finalizer(Exception __exception) { _sacrificing = false; return __exception; } } [HarmonyPatch(typeof(EnchantingTable), "GetFeatureCurrentValue")] private static class FeatureValuePatch { private static void Postfix(EnchantingFeature feature, ref Tuple<float, float> __result) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (_sacrificing && (int)feature == 0) { __result = Tuple.Create(float.NaN, float.NaN); } } } [HarmonyPatch(typeof(InventoryManagement), "RemoveExactItem")] private static class RemovedPatch { private static void Postfix(ItemData item, int __result) { if (_sacrificing && item != null && __result > 0) { Sacrificed.Add(Tuple.Create<ItemData, int>(item, __result)); } } } [HarmonyPatch(typeof(SacrificeUI), "OnEnable")] private static class ChestToggleUi { private static void Postfix(SacrificeUI __instance) { try { Toggle val = ((EnchantingTableUIPanelBase)__instance).AvailableItems?.SelectAllToggle; if (!((Object)(object)val == (Object)null)) { AddToggle(__instance, val, "IncludeChestsToggle", "Include trophies", 1, _trophies); AddToggle(__instance, val, "IncludeShardstonesToggle", "Include shardstones", 2, _shardstones); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Could not add the Sacrifice tab checkboxes: " + ex.Message)); } } } private static void AddToggle(SacrificeUI ui, Toggle selectAll, string name, string label, int row, ConfigEntry<bool> setting) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown Transform parent = ((Component)selectAll).transform.parent; if (!((Object)(object)parent.Find(name) != (Object)null)) { GameObject val = Object.Instantiate<GameObject>(((Component)selectAll).gameObject, parent, false); ((Object)val).name = name; RectTransform val2 = (RectTransform)val.transform; RectTransform val3 = (RectTransform)((Component)selectAll).transform; Vector2 anchoredPosition = val3.anchoredPosition; float num = row; Rect rect = val3.rect; val2.anchoredPosition = anchoredPosition + new Vector2(0f, num * (((Rect)(ref rect)).height + 4f)); Text[] componentsInChildren = val.GetComponentsInChildren<Text>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].text = label; } Toggle component = val.GetComponent<Toggle>(); component.onValueChanged = new ToggleEvent(); component.SetIsOnWithoutNotify(setting.Value); ((Selectable)component).interactable = true; ((UnityEvent<bool>)(object)component.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on) { setting.Value = on; RefreshSacrifice?.Invoke(ui, null); }); } } } [HarmonyPatch] private static class SacrificeListPatch { private static MethodBase TargetMethod() { return AccessTools.Method(AccessTools.TypeByName("EpicLoot.CraftingV2.EnchantingUIController"), "GetSacrificeItems", (Type[])null, (Type[])null); } private static void Prefix() { _buildingSacrificeList = true; } private static Exception Finalizer(Exception __exception) { _buildingSacrificeList = false; return __exception; } } public const string Guid = "digi.valheim.epiclootstoragematerials"; public const string Name = "Digi's Epic Loot Storage Materials"; public const string Version = "1.3.2"; private const string EpicLootGuid = "randyknapp.mods.epicloot"; private const string ProviderId = "pieter.nearbychests"; private static ConfigEntry<bool> _enabled; private static ConfigEntry<bool> _trophies; private static ConfigEntry<bool> _shardstones; private static ConfigEntry<bool> _perItemDouble; private static ManualLogSource _log; private static ConfigEntry<float> _range; private static readonly HashSet<Container> Containers = new HashSet<Container>(); private static MethodInfo _checkAccess; private static readonly List<Container> FrameChests = new List<Container>(); private static int _frameChestsFrame = -1; [ThreadStatic] private static bool _sacrificing; private static float _doubleChance; private static readonly List<Tuple<ItemData, int>> Sacrificed = new List<Tuple<ItemData, int>>(); private static readonly MethodInfo ProductsMethod = AccessTools.Method(AccessTools.TypeByName("EpicLoot.CraftingV2.EnchantingUIController"), "GetSacrificeProducts", new Type[1] { typeof(List<Tuple<ItemData, int>>) }, (Type[])null); private static readonly MethodInfo GiveMethod = AccessTools.Method(typeof(EnchantingTableUIPanelBase), "GiveItemsToPlayer", (Type[])null, (Type[])null); private static readonly MethodInfo RefreshSacrifice = AccessTools.Method(typeof(SacrificeUI), "RefreshAvailableItems", (Type[])null, (Type[])null); private static bool _buildingSacrificeList; private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.epiclootstoragematerials".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown MigrateConfig(); _enabled = base.Config.Bind("General", "Enabled", defaultValue: true, "The enchanting table also uses materials from nearby chests (for enchanting, upgrading, sacrificing, ...)."); _range = base.Config.Bind("General", "Range (m)", 50f, new ConfigDescription("Chests up to this many meters from you are used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())); _trophies = base.Config.Bind("General", "Chest trophies in Sacrifice", defaultValue: true, "The Sacrifice tab also lists trophies from nearby chests. Same as the \"Include trophies\" checkbox in that tab."); _shardstones = base.Config.Bind("General", "Chest shardstones in Sacrifice", defaultValue: false, "The Sacrifice tab also lists shardstones from nearby chests (they turn into shards). Off by default, so \"Select All\" cannot sacrifice your stored shardstones by accident. Same as the \"Include shardstones\" checkbox in that tab."); _perItemDouble = base.Config.Bind("Sacrifice", "Roll double output per item", defaultValue: true, "The table's chance of double output is rolled for each sacrificed item, instead of once for the whole batch (all doubled or nothing). Same average, no all-or-nothing gamble."); _log = base.Logger; _checkAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); Type type = AccessTools.TypeByName("EpicLoot.API"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "RegisterInventoryProvider", (Type[])null, (Type[])null)); if (methodInfo == null || _checkAccess == null) { base.Logger.LogError((object)"Disabled, Epic Loot's inventory provider API or Container.CheckAccess was not found."); return; } Harmony moduleHarmony = base.ModuleHarmony; moduleHarmony.PatchAll(typeof(ContainerRegistry)); moduleHarmony.PatchAll(typeof(SacrificePatch)); moduleHarmony.PatchAll(typeof(FeatureValuePatch)); moduleHarmony.PatchAll(typeof(RemovedPatch)); moduleHarmony.PatchAll(typeof(ChestToggleUi)); moduleHarmony.PatchAll(typeof(SacrificeListPatch)); bool flag = (bool)methodInfo.Invoke(null, new object[5] { "pieter.nearbychests", new Func<List<ItemData>>(ChestItems), new Func<string, int>(CountItem), new Func<string, int, int>(RemoveItem), new Func<ItemData, int, int>(RemoveExactItem) }); base.Logger.LogInfo((object)(flag ? "Epic Loot can now use materials from nearby chests." : "Epic Loot refused the inventory provider.")); } private static List<Container> UsableChests() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (_frameChestsFrame == Time.frameCount) { return FrameChests; } _frameChestsFrame = Time.frameCount; FrameChests.Clear(); Player localPlayer = Player.m_localPlayer; if (!_enabled.Value || (Object)(object)localPlayer == (Object)null) { return FrameChests; } Vector3 origin = ((Component)localPlayer).transform.position; float num = _range.Value * _range.Value; Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null); foreach (Container container in Containers) { Vector3 val = ((Component)container).transform.position - origin; if (((Vector3)(ref val)).sqrMagnitude <= num && IsUsable(container, localPlayer)) { FrameChests.Add(container); } } FrameChests.Sort(delegate(Container a, Container b) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) Vector3 val2 = ((Component)a).transform.position - origin; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; val2 = ((Component)b).transform.position - origin; return sqrMagnitude.CompareTo(((Vector3)(ref val2)).sqrMagnitude); }); return FrameChests; } private static bool IsUsable(Container chest, Player player) { if (!((Behaviour)chest).isActiveAndEnabled || chest.GetInventory() == null) { return false; } Piece componentInParent = ((Component)chest).GetComponentInParent<Piece>(); if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsPlacedByPlayer()) { return false; } if ((Object)(object)chest.m_wagon != (Object)null || (Object)(object)((Component)chest).GetComponentInParent<Ship>() != (Object)null) { return false; } if (!chest.IsOwner() || chest.IsInUse()) { return false; } return (bool)_checkAccess.Invoke(chest, new object[1] { player.GetPlayerID() }); } private static int CountItem(string name) { int num = 0; foreach (Container item in UsableChests()) { num += item.GetInventory().CountItems(name, -1, true); } return num; } private static List<ItemData> ChestItems() { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Invalid comparison between Unknown and I4 List<ItemData> list = new List<ItemData>(); bool flag = _shardstones.Value && _buildingSacrificeList; if (!_trophies.Value && !flag) { return list; } foreach (Container item in UsableChests()) { foreach (ItemData allItem in item.GetInventory().GetAllItems()) { if (allItem?.m_shared != null) { if (_trophies.Value && (int)allItem.m_shared.m_itemType == 13) { list.Add(allItem); } else if (flag && API.IsShardStone(allItem)) { list.Add(allItem); } } } } return list; } private static int RemoveExactItem(ItemData item, int amount) { if (item == null || amount <= 0) { return 0; } foreach (Container item2 in UsableChests()) { Inventory inventory = item2.GetInventory(); if (inventory.ContainsItem(item)) { int stack = item.m_stack; inventory.RemoveItem(item, Mathf.Min(amount, stack)); return stack - (inventory.ContainsItem(item) ? item.m_stack : 0); } } return 0; } private static int RemoveItem(string name, int amount) { int num = 0; foreach (Container item in UsableChests()) { if (num >= amount) { break; } Inventory inventory = item.GetInventory(); int num2 = inventory.CountItems(name, -1, true); if (num2 > 0) { inventory.RemoveItem(name, Mathf.Min(num2, amount - num), -1, true); num += num2 - inventory.CountItems(name, -1, true); } } return num; } } } namespace EpicLootFixes { public class Plugin : DigiModule { private sealed class LuckyBefore { public object Owner; public float Stamp; } public const string Guid = "digi.valheim.epiclootfixes"; public const string Name = "Digi's Epic Loot Fixes"; public const string Version = "1.2.0"; private const string EpicLootGuid = "randyknapp.mods.epicloot"; private static ConfigEntry<bool> _eitrTooltip; private static ConfigEntry<bool> _luckyMessage; private static ConfigEntry<bool> _luckyBirds; internal static ManualLogSource Log; private static ConfigEntry<MessageType> _luckyPosition; private static readonly FieldRef<MagicTooltip, ItemData> Item = AccessTools.FieldRefAccess<MagicTooltip, ItemData>("<item>P"); private static readonly FieldRef<MagicTooltip, MagicItem> Magic = AccessTools.FieldRefAccess<MagicTooltip, MagicItem>("<magicItem>P"); private static readonly FieldRef<MagicTooltip, string> MagicColor = AccessTools.FieldRefAccess<MagicTooltip, string>("magicColor"); private static readonly FieldRef<StringBuilder> Text = AccessTools.StaticFieldRefAccess<StringBuilder>(AccessTools.Field(typeof(MagicTooltip), "text")); private const string LuckyRpc = "Digi_LuckyLoot"; private const float LuckyRange = 100f; private static readonly Type Lucky = AccessTools.TypeByName("EpicLoot.MagicItemEffects.Shards.LuckyLoot"); private static readonly MethodInfo LuckyPostfix = AccessTools.Method(AccessTools.TypeByName("EpicLoot.MagicItemEffects.Shards.LuckyLoot+LuckyLoot_CharacterDrop_GenerateDropList_Patch"), "Postfix", (Type[])null, (Type[])null); private static readonly FieldInfo PendingOwner = ((Lucky == null) ? null : AccessTools.Field(Lucky, "_pendingOwner")); private static readonly FieldInfo PendingRolls = ((Lucky == null) ? null : AccessTools.Field(Lucky, "_pendingRolls")); private static readonly FieldInfo PendingStamp = ((Lucky == null) ? null : AccessTools.Field(Lucky, "_pendingStamp")); private static readonly MethodInfo BestNearbyValue = ((Lucky == null) ? null : AccessTools.Method(Lucky, "BestNearbyValue", (Type[])null, (Type[])null)); private static readonly MethodInfo Multiplier = ((Lucky == null) ? null : AccessTools.Method(Lucky, "Multiplier", (Type[])null, (Type[])null)); private static readonly FieldRef<CharacterDrop, Character> CreatureOf = AccessTools.FieldRefAccess<CharacterDrop, Character>("m_character"); private static int _birdMultiplier; private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.epiclootfixes".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_010e: Expected O, but got Unknown MigrateConfig(); _eitrTooltip = base.Config.Bind("Tooltips", "Show eitr cost on enchanted items", defaultValue: true, "Show the eitr cost of enchanted staffs and Spellsword weapons (Epic Loot hides it)."); _luckyMessage = base.Config.Bind("Lucky Loot", "Show Lucky Loot message", defaultValue: true, "Show a message when the Lucky Loot shard multiplies a creature's drops (e.g. \"Lucky Loot! Boar: 6x drops, +2 magic item rolls\")."); _luckyPosition = base.Config.Bind<MessageType>("Lucky Loot", "Message position", (MessageType)2, "Center: big message in the middle of the screen. TopLeft: small line in the top left corner."); _luckyBirds = base.Config.Bind("Lucky Loot", "Lucky Loot on birds", defaultValue: true, "Birds (crows, seagulls) can get Lucky Loot too, with the same chance and drop multiplier as creatures. (Epic Loot itself only does creatures.)"); Log = base.Logger; Harmony val = PatchModule(typeof(Plugin)); if (LuckyPostfix != null && PendingOwner != null && PendingRolls != null && PendingStamp != null) { val.Patch((MethodBase)LuckyPostfix, new HarmonyMethod(typeof(Plugin), "BeforeLucky", (Type[])null), new HarmonyMethod(typeof(Plugin), "AfterLucky", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { base.Logger.LogWarning((object)"Lucky Loot message is off: this Epic Loot version has no Lucky Loot shard code we know."); } } private static void BeforeLucky(out LuckyBefore __state) { __state = new LuckyBefore { Owner = PendingOwner.GetValue(null), Stamp = (float)PendingStamp.GetValue(null) }; } private static void AfterLucky(CharacterDrop __0, LuckyBefore __state) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__0 == (Object)null || (Object)(object)CreatureOf.Invoke(__0) == (Object)null || ZRoutedRpc.instance == null) { return; } object? value = PendingOwner.GetValue(null); CharacterDrop val = (CharacterDrop)((value is CharacterDrop) ? value : null); float num = (float)PendingStamp.GetValue(null); if (!((Object)(object)val != (Object)(object)__0) && (__state.Owner != val || num != __state.Stamp)) { Vector3 position = ((Component)CreatureOf.Invoke(__0)).transform.position; int multiplier = 0; if (BestNearbyValue != null && Multiplier != null) { multiplier = (int)Multiplier.Invoke(null, new object[1] { (float)BestNearbyValue.Invoke(null, new object[1] { position }) }); } SendLucky(position, CreatureOf.Invoke(__0).m_name ?? "", multiplier, (int)PendingRolls.GetValue(null)); } } private static void SendLucky(Vector3 position, string name, int multiplier, int rolls) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(position); val.Write(name); val.Write(multiplier); val.Write(rolls); ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Digi_LuckyLoot", new object[1] { val }); } } [HarmonyPatch(typeof(DropOnDestroyed), "OnDestroyed")] [HarmonyPrefix] private static void BirdDropsBefore(DropOnDestroyed __instance) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) _birdMultiplier = 0; if (!_luckyBirds.Value || BestNearbyValue == null || Multiplier == null || ((Object)(object)((Component)__instance).GetComponentInParent<RandomFlyingBird>() == (Object)null && (Object)(object)((Component)__instance).GetComponentInChildren<RandomFlyingBird>() == (Object)null)) { return; } Vector3 position = ((Component)__instance).transform.position; float num = (float)BestNearbyValue.Invoke(null, new object[1] { position }); if (num <= 0f || Random.value >= num * 0.01f) { return; } int num2 = (int)Multiplier.Invoke(null, new object[1] { num }); if (num2 > 1) { _birdMultiplier = num2; string text = Utils.GetPrefabName(((Component)__instance).gameObject); if (text == "Seagal") { text = "Seagull"; } ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"Lucky Loot proc on bird {text}: {num2}x drops (value {num})."); } SendLucky(position, text, num2, 0); } } [HarmonyPatch(typeof(DropOnDestroyed), "OnDestroyed")] [HarmonyFinalizer] private static Exception BirdDropsAfter(Exception __exception) { _birdMultiplier = 0; return __exception; } [HarmonyPatch(typeof(DropTable), "GetDropList", new Type[] { typeof(int) })] [HarmonyPostfix] private static void MultiplyBirdDrops(ref List<GameObject> __result) { if (_birdMultiplier > 1 && __result != null && __result.Count != 0) { List<GameObject> list = new List<GameObject>(__result); int num = Mathf.Min(_birdMultiplier, Mathf.Max(1, 100 / list.Count)); for (int i = 1; i < num; i++) { __result.AddRange(list); } _birdMultiplier = 0; } } [HarmonyPatch(typeof(Game), "Start")] [HarmonyPostfix] private static void RegisterRpc() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.Register<ZPackage>("Digi_LuckyLoot", (Action<long, ZPackage>)OnLucky); } } private static void OnLucky(long sender, ZPackage package) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) Vector3 val = package.ReadVector3(); string text = package.ReadString(); int num = package.ReadInt(); int num2 = package.ReadInt(); Player localPlayer = Player.m_localPlayer; if (_luckyMessage.Value && !((Object)(object)localPlayer == (Object)null) && !(Vector3.Distance(((Component)localPlayer).transform.position, val) > 100f)) { string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text); string text3 = "Lucky Loot! " + text2 + ":" + ((num > 1) ? $" {num}x drops" : " more drops") + ((num2 > 0) ? string.Format(", +{0} magic item roll{1}", num2, (num2 == 1) ? "" : "s") : ""); ((Character)localPlayer).Message(_luckyPosition.Value, text3, 0, (Sprite)null, false); } } [HarmonyPatch(typeof(MagicTooltip), "AddEitrUse")] [HarmonyPrefix] private static bool AddEitrUse(MagicTooltip __instance) { if (!_eitrTooltip.Value) { return true; } ItemData val = Item.Invoke(__instance); MagicItem val2 = Magic.Invoke(__instance); StringBuilder stringBuilder = Text.Invoke(); if (val?.m_shared == null || val2 == null || stringBuilder == null) { return true; } Attack attack = val.m_shared.m_attack; bool flag = val2.HasEffect(MagicEffectType.SpellSword, false, false); if (attack.m_attackEitr <= 0f && !flag) { return false; } float num = (1f - val2.GetTotalEffectValue(MagicEffectType.ModifyAttackEitrUse, 0.01f, true)) * attack.m_attackEitr; if (flag) { num += Spellsword.GetAdditionalSpellswordAttackEitr(attack.m_attackStamina); } bool flag2 = flag || val2.HasEffect(MagicEffectType.ModifyAttackEitrUse, false, false) || val2.HasEffect(MagicEffectType.DoubleMagicShot, false, false); stringBuilder.Append(string.Format("\n$item_eitruse: <color={0}>{1:0.#}</color>", flag2 ? MagicColor.Invoke(__instance) : "orange", num)); return false; } } } namespace EnchantGlowToggle { public class Plugin : DigiModule { private enum Match { SameItem, AnyAppearance, Off } public const string Guid = "digi.valheim.enchantglowtoggle"; public const string Name = "Digi's Enchant Glow Toggle"; public const string Version = "1.3.3"; private const string EpicLootGuid = "randyknapp.mods.epicloot"; private const string EquipmentSlotsGuid = "cdev.equipmentslots"; private const string LookKey = "CDEV_EquipmentSlots_look"; private const string GlowFx = "GlowingAura"; private static readonly int HiddenKey = StringExtensionMethods.GetStableHashCode("pieter_enchantglowtoggle_hidden"); private static readonly int FxKey = StringExtensionMethods.GetStableHashCode("pieter_enchantglowtoggle_fx"); private static ConfigEntry<bool> _legendaryFallback; private static ConfigEntry<bool> _shareAuras; [ThreadStatic] private static bool _publishing; private static readonly Dictionary<Player, Dictionary<string, GameObject>> RemoteFx = new Dictionary<Player, Dictionary<string, GameObject>>(); private static ConfigEntry<Match> _appearance; private static ConfigEntry<bool> _hideGlowing; private static ConfigEntry<bool> _hideAll; private static ConfigEntry<bool> _hideTextures; private static ConfigEntry<bool> _effectsBehind; private static ConfigEntry<bool> _texturesBehind; private static MethodInfo _refreshFx; private static readonly FieldRef<Humanoid, ItemData> Helmet = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_helmetItem"); private static readonly FieldRef<Humanoid, ItemData> Chest = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_chestItem"); private static readonly FieldRef<Humanoid, ItemData> Legs = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_legItem"); private static readonly FieldRef<Humanoid, ItemData> Cape = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_shoulderItem"); private static readonly FieldRef<Humanoid, VisEquipment> Vis = AccessTools.FieldRefAccess<Humanoid, VisEquipment>("m_visEquipment"); private static readonly string[] VisHashFields = new string[4] { "m_currentHelmetItemHash", "m_currentChestItemHash", "m_currentLegItemHash", "m_currentShoulderItemHash" }; [ThreadStatic] private static Player _dressing; private static readonly Dictionary<Player, int> KnownMasks = new Dictionary<Player, int>(); private float _nextCheck; private static string _lastDiagnostics; private static ManualLogSource _log; private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.enchantglowtoggle".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { MigrateConfig(); _appearance = base.Config.Bind("Appearance tab", "When to hide", Match.SameItem, "When a choice in the Appearance tab hides an item's enchanted look. SameItem = only when you pick the same item as its appearance. AnyAppearance = whenever the slot has an appearance. Off = never. Other players with this mod see it the same way."); _effectsBehind = base.Config.Bind("Appearance tab", "Hide glow and effects", defaultValue: true, "Hide the item's enchantment effects (like the Glowing aura) when the rule above applies."); _texturesBehind = base.Config.Bind("Appearance tab", "Hide legendary texture", defaultValue: true, "Hide the item's legendary look (like the blue Heimdall armor) when the rule above applies."); _hideGlowing = base.Config.Bind("Always", "Hide Glowing aura", defaultValue: false, "Never show the light and sparkles of the Glowing enchantment, on anyone. Only on your screen."); _hideAll = base.Config.Bind("Always", "Hide all enchantment effects", defaultValue: false, "Never show any effect of Epic Loot enchantments or legendary items, on anyone. Only on your screen."); _hideTextures = base.Config.Bind("Always", "Hide legendary textures", defaultValue: false, "Never show the special look of Epic Loot legendary items, on anyone. Only on your screen."); _legendaryFallback = base.Config.Bind("Effects", "Show enchant effects on legendaries", defaultValue: true, "Legendary items that have no effect of their own show the effect of their enchantments (like Glowing). Epic Loot normally shows nothing for them."); _shareAuras = base.Config.Bind("Effects", "Show auras on other players", defaultValue: true, "See the enchantment auras (like Glowing) of other players who have this mod. Epic Loot alone only shows your own."); _legendaryFallback.SettingChanged += delegate { RefreshAll(); }; _log = base.Logger; Type type = AccessTools.TypeByName("EpicLoot.VisEquipment_Patch"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "GetEquipFxName", (Type[])null, (Type[])null)); MethodInfo methodInfo2 = ((type == null) ? null : AccessTools.Method(type, "SetTextureOverrides", (Type[])null, (Type[])null)); _refreshFx = ((type == null) ? null : AccessTools.Method(type, "RefreshPlayerFx", (Type[])null, (Type[])null)); if (methodInfo == null || methodInfo2 == null || _refreshFx == null) { base.Logger.LogError((object)"Disabled, Epic Loot's equipment visuals were not found."); return; } Harmony moduleHarmony = base.ModuleHarmony; moduleHarmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, Hook("EquipFxPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); moduleHarmony.Patch((MethodBase)methodInfo2, Hook("TexturePrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); moduleHarmony.Patch((MethodBase)_refreshFx, Hook("DressPlayerPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, Hook("DressFinalizer"), (HarmonyMethod)null); string[] array = new string[2] { "AttachItem_Postfix", "AttachArmor_Postfix" }; foreach (string text in array) { MethodInfo methodInfo3 = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (methodInfo3 != null) { moduleHarmony.Patch((MethodBase)methodInfo3, Hook("DressVisPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, Hook("DressFinalizer"), (HarmonyMethod)null); } } _appearance.SettingChanged += delegate { RefreshAll(); }; _effectsBehind.SettingChanged += delegate { RefreshAll(); }; _texturesBehind.SettingChanged += delegate { RefreshAll(); }; _hideGlowing.SettingChanged += delegate { RefreshAll(); }; _hideAll.SettingChanged += delegate { RefreshAll(); }; _hideTextures.SettingChanged += delegate { RefreshAll(); }; base.Logger.LogInfo((object)"Enchantment looks hide behind Appearance tab choices (shared with other players)."); } private static HarmonyMethod Hook(string name) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return new HarmonyMethod(typeof(Plugin).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic)); } private static int SlotBit(ItemData item) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 ItemType? val = item?.m_shared?.m_itemType; if (val.HasValue) { ItemType valueOrDefault = val.GetValueOrDefault(); if ((int)valueOrDefault <= 7) { if ((int)valueOrDefault == 6) { return 1; } if ((int)valueOrDefault == 7) { return 2; } } else { if ((int)valueOrDefault == 11) { return 4; } if ((int)valueOrDefault == 17) { return 8; } } } return 0; } private static int LocalMask(Player player) { if (_appearance.Value == Match.Off || player?.m_customData == null) { return 0; } if (!player.m_customData.TryGetValue("CDEV_EquipmentSlots_look", out var value) || string.IsNullOrEmpty(value)) { return 0; } string[] array = value.Split(new char[1] { '|' }); ItemData[] array2 = (ItemData[])(object)new ItemData[4] { Helmet.Invoke((Humanoid)(object)player), Chest.Invoke((Humanoid)(object)player), Legs.Invoke((Humanoid)(object)player), Cape.Invoke((Humanoid)(object)player) }; int num = 0; for (int i = 0; i < array2.Length && i < array.Length; i++) { if (array[i].Length != 0 && array2[i] != null && (_appearance.Value == Match.AnyAppearance || ((Object)(object)array2[i].m_dropPrefab != (Object)null && ((Object)array2[i].m_dropPrefab).name == array[i]))) { num |= 1 << i; } } return (_effectsBehind.Value ? num : 0) | (_texturesBehind.Value ? (num << 4) : 0); } private static int MaskOf(Player player) { if ((Object)(object)player == (Object)null) { return 0; } if ((Object)(object)player == (Object)(object)Player.m_localPlayer) { return LocalMask(player); } ZNetView component = ((Component)player).GetComponent<ZNetView>(); ZDO obj = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (obj == null) { return 0; } return obj.GetInt(HiddenKey, 0); } private static bool Hidden(Player player, ItemData item, bool texture) { if ((Object)(object)player == (Object)null && item != null && (Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).IsItemEquiped(item)) { player = Player.m_localPlayer; } int num = SlotBit(item) << (texture ? 4 : 0); if (num != 0) { return (MaskOf(player) & num) != 0; } return false; } private static void DressPlayerPrefix(Player player) { _dressing = player; } private static void DressVisPrefix(VisEquipment __0) { _dressing = (((Object)(object)__0 != (Object)null) ? ((Component)__0).GetComponent<Player>() : null); } private static Exception DressFinalizer(Exception __exception) { _dressing = null; return __exception; } private static void EquipFxPostfix(ItemData equippedItem, ref string __result, ref FxAttachMode mode) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected I4, but got Unknown if (equippedItem == null) { return; } MagicItem val = default(MagicItem); if (string.IsNullOrEmpty(__result) && _legendaryFallback.Value && ItemDataExtensions.IsMagic(equippedItem, ref val) && val.IsUniqueLegendary() && string.IsNullOrEmpty(val.GetLegendaryInfo()?.EquipFx)) { FxAttachMode val2 = default(FxAttachMode); string firstEquipEffect = val.GetFirstEquipEffect(ref val2); if (!string.IsNullOrEmpty(firstEquipEffect)) { __result = firstEquipEffect; mode = (FxAttachMode)(int)val2; } } if (!string.IsNullOrEmpty(__result) && ((!_publishing && (_hideAll.Value || (_hideGlowing.Value && __result == "GlowingAura"))) || Hidden(_dressing, equippedItem, texture: false))) { __result = null; } } private static string LocalAuras(Player player) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.Ordinal); _publishing = true; try { FxAttachMode val = default(FxAttachMode); foreach (ItemData equippedItem in ((Humanoid)player).GetInventory().GetEquippedItems()) { if (equippedItem != null && ItemDataExtensions.IsMagic(equippedItem)) { string equipFxName = VisEquipment_Patch.GetEquipFxName(equippedItem, ref val); if ((int)val == 1 && !string.IsNullOrEmpty(equipFxName)) { sortedSet.Add(equipFxName); } } } } finally { _publishing = false; } return string.Join(",", sortedSet); } private static void LogDiagnostics(Player player, string auras) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); MagicItem val = default(MagicItem); FxAttachMode val2 = default(FxAttachMode); FxAttachMode val3 = default(FxAttachMode); foreach (ItemData equippedItem in ((Humanoid)player).GetInventory().GetEquippedItems()) { if (equippedItem != null && ItemDataExtensions.IsMagic(equippedItem, ref val)) { string firstEquipEffect = val.GetFirstEquipEffect(ref val2); string equipFxName = VisEquipment_Patch.GetEquipFxName(equippedItem, ref val3); stringBuilder.Append($"\n {equippedItem.m_shared.m_name}: legendary={val.IsUniqueLegendary()} " + $"legendaryFx='{val.GetLegendaryInfo()?.EquipFx}' firstEffectFx='{firstEquipEffect}'({val2}) " + string.Format("shown='{0}'({1}) effects=[{2}]", equipFxName, val3, string.Join(",", val.Effects.Select((MagicItemEffect e) => e.EffectType)))); } } string arg = string.Join(",", from n in auras.Split(new char[1] { ',' }) where n.Length > 0 select n + ":" + (((Object)(object)((Component)player).transform.Find(n) != (Object)null) ? "attached" : "MISSING")); string text = $"Your auras: [{auras}] on character: [{arg}]{stringBuilder}"; if (!(text == _lastDiagnostics)) { _lastDiagnostics = text; ManualLogSource log = _log; if (log != null) { log.LogInfo((object)text); } } } private static void UpdateRemoteAuras(Player player, string shared) { if (!RemoteFx.TryGetValue(player, out var value)) { value = (RemoteFx[player] = new Dictionary<string, GameObject>()); } HashSet<string> hashSet = new HashSet<string>((_shareAuras.Value && !_hideAll.Value && !string.IsNullOrEmpty(shared)) ? (from n in shared.Split(new char[1] { ',' }) where n.Length > 0 && (!_hideGlowing.Value || !(n == "GlowingAura")) select n) : Enumerable.Empty<string>()); foreach (string item in value.Keys.ToList()) { if (!hashSet.Contains(item) || !((Object)(object)value[item] != (Object)null)) { if ((Object)(object)value[item] != (Object)null) { Object.Destroy((Object)(object)value[item]); } value.Remove(item); } } foreach (string item2 in hashSet) { if (value.ContainsKey(item2)) { continue; } GameObject val = EpicLoot.LoadAsset<GameObject>(item2); if (!((Object)(object)val == (Object)null)) { bool forceDisableInit = ZNetView.m_forceDisableInit; ZNetView.m_forceDisableInit = true; try { GameObject val2 = Object.Instantiate<GameObject>(val, ((Component)player).transform, false); ((Object)val2).name = item2; value[item2] = val2; } finally { ZNetView.m_forceDisableInit = forceDisableInit; } } } } private static bool TexturePrefix(VisEquipment __0, ItemData __3) { if (_hideTextures.Value) { return false; } return !Hidden(((Object)(object)__0 != (Object)null) ? ((Component)__0).GetComponent<Player>() : null, __3, texture: true); } private void Update() { if (Time.time < _nextCheck || (Object)(object)Player.m_localPlayer == (Object)null) { return; } _nextCheck = Time.time + 0.5f; Player localPlayer = Player.m_localPlayer; int num = LocalMask(localPlayer); ZNetView component = ((Component)localPlayer).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val != null && component.IsOwner() && val.GetInt(HiddenKey, 0) != num) { val.Set(HiddenKey, num, false); } if (val != null && component.IsOwner()) { string text = LocalAuras(localPlayer); if (val.GetString(FxKey, "") != text) { val.Set(FxKey, text); } LogDiagnostics(localPlayer, text); } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null) { continue; } if ((Object)(object)allPlayer != (Object)(object)localPlayer) { ZNetView component2 = ((Component)allPlayer).GetComponent<ZNetView>(); ZDO val2 = (((Object)(object)component2 != (Object)null && component2.IsValid()) ? component2.GetZDO() : null); UpdateRemoteAuras(allPlayer, ((val2 != null) ? val2.GetString(FxKey, "") : null) ?? ""); } int num2 = MaskOf(allPlayer); if (!KnownMasks.TryGetValue(allPlayer, out var value) || value != num2) { bool num3 = !KnownMasks.ContainsKey(allPlayer); KnownMasks[allPlayer] = num2; if (!num3 || num2 != 0) { Redress(allPlayer); } } } if (KnownMasks.Count > 32) { List<Player> list = new List<Player>(); foreach (Player key in KnownMasks.Keys) { if ((Object)(object)key == (Object)null) { list.Add(key); } } foreach (Player item in list) { KnownMasks.Remove(item); } } foreach (Player item2 in RemoteFx.Keys.Where((Player p) => (Object)(object)p == (Object)null).ToList()) { RemoteFx.Remove(item2); } } private static void RefreshAll() { foreach (Player allPlayer in Player.GetAllPlayers()) { Redress(allPlayer); } } private static void Redress(Player player) { if ((Object)(object)player == (Object)null) { return; } try { VisEquipment val = Vis.Invoke((Humanoid)(object)player); if ((Object)(object)val != (Object)null) { string[] visHashFields = VisHashFields; foreach (string text in visHashFields) { Traverse.Create((object)val).Field(text).SetValue((object)(-1)); } } _refreshFx.Invoke(null, new object[1] { player }); } catch (Exception) { } } } }