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 RunicInventory v1.1.11
plugins/RunicInventory/RunicInventory.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Runic.Foundation.Core; using Runic.Localization; using RunicInventory.Api; using RunicInventory.Core; using RunicInventory.Integration; using UnityEngine; using UnityEngine.EventSystems; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Runic Inventory")] [assembly: AssemblyDescription("Labeled native-row equipment and quick slots with automatic empty-role equip, lossless armor swaps, locks, sort, and pickup controls for Valheim.")] [assembly: AssemblyCompany("Chazman")] [assembly: AssemblyProduct("Runic Inventory")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyFileVersion("1.1.10.0")] [assembly: AssemblyInformationalVersion("1.1.10")] [assembly: InternalsVisibleTo("RunicInventory.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.1.10.0")] [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 Runic.Localization { internal sealed class LanguageCatalog { private readonly Dictionary<string, string> _english; private Dictionary<string, string> _selected = new Dictionary<string, string>(); private readonly Func<string, string> _read; private string _language; internal LanguageCatalog(string english, Func<string, string> read) { _english = Parse(english); _read = read; } internal static Dictionary<string, string> Parse(string json) { if (string.IsNullOrWhiteSpace(json)) { return new Dictionary<string, string>(); } if (json.Length > 2097152) { throw new InvalidDataException("Language file exceeds 2 MiB."); } DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(Dictionary<string, string>), new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true }); using MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json.TrimStart('\ufeff'))); return ((Dictionary<string, string>)dataContractJsonSerializer.ReadObject(stream)) ?? new Dictionary<string, string>(); } internal void Select(string language) { language = (string.IsNullOrWhiteSpace(language) ? "English" : language); if (string.Equals(language, _language, StringComparison.Ordinal)) { return; } _language = language; _selected = new Dictionary<string, string>(); if (language.IndexOfAny(new char[4] { '/', '\\', ':', '\0' }) >= 0 || language == "." || language == "..") { return; } try { _selected = Parse(_read(language)); } catch (Exception) { } } internal string Get(string key) { _english.TryGetValue(key, out var value); if (_selected.TryGetValue(key, out var value2) && !string.IsNullOrWhiteSpace(value2) && (value == null || SameFields(value, value2))) { return value2; } return value ?? key; } internal string English(string key) { if (!_english.TryGetValue(key, out var value)) { return key; } return value; } internal string TranslateEnglish(string text) { foreach (KeyValuePair<string, string> item in _english) { if (item.Value == text) { return Get(item.Key); } } return text; } internal string Format(string key, params object[] args) { _english.TryGetValue(key, out var value); string text = Get(key); if (value != null && !SameFields(value, text)) { text = value; } try { return string.Format(CultureInfo.CurrentCulture, text, args); } catch (FormatException) { try { return string.Format(CultureInfo.CurrentCulture, value ?? key, args); } catch (FormatException) { return value ?? key; } } } private static bool SameFields(string a, string b) { try { return Fields(a).SetEquals(Fields(b)); } catch (FormatException) { return false; } } private static HashSet<int> Fields(string text) { HashSet<int> hashSet = new HashSet<int>(); for (int i = 0; i < text.Length; i++) { if (text[i] == '}') { if (i + 1 >= text.Length || text[i + 1] != '}') { throw new FormatException(); } i++; } else { if (text[i] != '{') { continue; } if (i + 1 < text.Length && text[i + 1] == '{') { i++; continue; } int num = ++i; for (; i < text.Length && char.IsDigit(text[i]); i++) { } if (num == i || !int.TryParse(text.Substring(num, i - num), out var result)) { throw new FormatException(); } hashSet.Add(result); for (; i < text.Length && text[i] != '}'; i++) { if (text[i] == '{') { throw new FormatException(); } } if (i == text.Length) { throw new FormatException(); } } } return hashSet; } } internal static class RunicText { private static readonly Assembly Owner = typeof(RunicText).Assembly; private static readonly string Module = Owner.GetName().Name; private static readonly LanguageCatalog Catalog = Create(); private static LanguageCatalog Create() { using Stream stream = Owner.GetManifestResourceStream("Runic.Localization.English.json"); using StreamReader streamReader = ((stream == null) ? null : new StreamReader(stream)); return new LanguageCatalog(streamReader?.ReadToEnd() ?? "{}", ReadLanguage); } private static string ReadLanguage(string language) { string path = Path.Combine(Path.Combine(Path.GetDirectoryName(Owner.Location), "Translations", Module), language + ".json"); if (!File.Exists(path)) { return null; } return File.ReadAllText(path); } private static void SelectLanguage() { string language = "English"; try { Localization instance = Localization.instance; language = ((instance != null) ? instance.GetSelectedLanguage() : null) ?? "English"; } catch { } Catalog.Select(language); } internal static string Get(string key) { SelectLanguage(); return Catalog.Get(key); } internal static string English(string key) { return Catalog.English(key); } internal static string TranslateEnglish(string text) { SelectLanguage(); return Catalog.TranslateEnglish(text); } internal static string Format(string key, params object[] args) { SelectLanguage(); return Catalog.Format(key, args); } } } namespace Runic.Foundation.Core { public static class RunicIdentifier { public static bool IsValid(string value) { if (string.IsNullOrEmpty(value) || value.Length > 128) { return false; } bool flag = false; bool flag2 = false; foreach (char c in value) { if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { flag = true; flag2 = false; continue; } switch (c) { case '-': if (!flag || flag2) { return false; } flag2 = true; break; case '.': if (!flag || flag2) { return false; } flag = false; flag2 = false; break; default: return false; } } if (flag) { return !flag2; } return false; } public static string Require(string value, string parameterName) { if (!IsValid(value)) { throw new ArgumentException("Runic identifiers must be lowercase dot-separated tokens containing only ASCII letters, digits, and internal hyphens.", parameterName); } return value; } } public sealed class InputChord : IEquatable<InputChord> { private readonly IReadOnlyList<string> _modifiers; public string DeviceId { get; } public string PrimaryControl { get; } public IReadOnlyList<string> Modifiers => _modifiers; public string CanonicalId { get; } public InputChord(string deviceId, string primaryControl, IEnumerable<string> modifiers = null) { DeviceId = RunicIdentifier.Require(deviceId, "deviceId"); PrimaryControl = RequireControl(primaryControl, "primaryControl"); string text = CanonicalizeControl(PrimaryControl); SortedDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>(StringComparer.Ordinal); if (modifiers != null) { foreach (string modifier in modifiers) { string text2 = RequireControl(modifier, "modifiers"); string text3 = CanonicalizeControl(text2); if (string.Equals(text3, text, StringComparison.Ordinal)) { throw new ArgumentException("The primary control cannot also be a modifier.", "modifiers"); } if (sortedDictionary.ContainsKey(text3)) { throw new ArgumentException("Duplicate input modifier: " + text2, "modifiers"); } sortedDictionary.Add(text3, text2); } } string[] array = new string[sortedDictionary.Count]; int num = 0; foreach (string value in sortedDictionary.Values) { array[num++] = value; } _modifiers = Array.AsReadOnly(array); StringBuilder stringBuilder = new StringBuilder(DeviceId).Append(':'); foreach (string key in sortedDictionary.Keys) { stringBuilder.Append(key).Append('+'); } stringBuilder.Append(text); CanonicalId = stringBuilder.ToString(); } public bool Equals(InputChord other) { if (other != null) { return string.Equals(CanonicalId, other.CanonicalId, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { return Equals(obj as InputChord); } public override int GetHashCode() { return StringComparer.Ordinal.GetHashCode(CanonicalId); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < _modifiers.Count; i++) { if (i > 0) { stringBuilder.Append(" + "); } stringBuilder.Append(_modifiers[i]); } if (_modifiers.Count > 0) { stringBuilder.Append(" + "); } return stringBuilder.Append(PrimaryControl).Append(" [").Append(DeviceId) .Append(']') .ToString(); } private static string RequireControl(string value, string parameterName) { if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("An input control name is required.", parameterName); } string text = value.Trim(); if (text.Length > 64) { throw new ArgumentException("Input control names cannot exceed 64 characters.", parameterName); } string text2 = text; foreach (char c in text2) { if (char.IsControl(c) || c == ':' || c == '+' || c == '|') { throw new ArgumentException("The input control name contains a reserved character.", parameterName); } } if (CanonicalizeControl(text).Length == 0) { throw new ArgumentException("The input control name has no canonical characters.", parameterName); } return text; } private static string CanonicalizeControl(string value) { StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { if (!char.IsWhiteSpace(c) && c != '_' && c != '-') { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } } public sealed class KeybindingDescriptor { public string ModuleId { get; } public string BindingId { get; } public string QualifiedId => ModuleId + "/" + BindingId; public string DisplayName { get; } public InputChord Chord { get; } public string Context { get; } public KeybindingDescriptor(string moduleId, string bindingId, string displayName, InputChord chord, string context = "gameplay") { ModuleId = RunicIdentifier.Require(moduleId, "moduleId"); BindingId = RunicIdentifier.Require(bindingId, "bindingId"); Context = RunicIdentifier.Require(context, "context"); if (string.IsNullOrWhiteSpace(displayName)) { throw new ArgumentException("A keybinding display name is required.", "displayName"); } Chord = chord ?? throw new ArgumentNullException("chord"); DisplayName = displayName.Trim(); } } public sealed class KeybindingConflict { public InputChord Chord { get; } public IReadOnlyList<KeybindingDescriptor> Bindings { get; } internal KeybindingConflict(InputChord chord, IReadOnlyList<KeybindingDescriptor> bindings) { Chord = chord; Bindings = bindings; } } public enum KeybindingChangeKind { Registered, Unregistered } public sealed class KeybindingsChangedEventArgs : EventArgs { public KeybindingChangeKind Kind { get; } public KeybindingDescriptor Binding { get; } public KeybindingConflict Conflict { get; } internal KeybindingsChangedEventArgs(KeybindingChangeKind kind, KeybindingDescriptor binding, KeybindingConflict conflict) { Kind = kind; Binding = binding; Conflict = conflict; } } public sealed class KeybindingConflictRegistry { private sealed class BindingEntry { internal KeybindingDescriptor Binding { get; } internal long Token { get; } internal BindingEntry(KeybindingDescriptor binding, long token) { Binding = binding; Token = token; } } private readonly object _sync = new object(); private readonly Dictionary<string, BindingEntry> _byQualifiedId = new Dictionary<string, BindingEntry>(StringComparer.Ordinal); private readonly Dictionary<string, List<BindingEntry>> _byChord = new Dictionary<string, List<BindingEntry>>(StringComparer.Ordinal); private long _nextToken; public event EventHandler<KeybindingsChangedEventArgs> Changed; public KeybindingRegistration Register(KeybindingDescriptor binding) { if (binding == null) { throw new ArgumentNullException("binding"); } long token; KeybindingConflict conflict; lock (_sync) { if (_byQualifiedId.ContainsKey(binding.QualifiedId)) { throw new InvalidOperationException("A keybinding is already registered as '" + binding.QualifiedId + "'."); } if (_nextToken == long.MaxValue) { throw new InvalidOperationException("The keybinding registration token space is exhausted."); } token = ++_nextToken; BindingEntry bindingEntry = new BindingEntry(binding, token); _byQualifiedId.Add(binding.QualifiedId, bindingEntry); if (!_byChord.TryGetValue(binding.Chord.CanonicalId, out var value)) { value = new List<BindingEntry>(); _byChord.Add(binding.Chord.CanonicalId, value); } value.Add(bindingEntry); conflict = CreateConflictLocked(binding.Chord.CanonicalId); } RaiseChanged(new KeybindingsChangedEventArgs(KeybindingChangeKind.Registered, binding, conflict)); return new KeybindingRegistration(this, binding, token); } public bool Unregister(string moduleId, string bindingId) { RunicIdentifier.Require(moduleId, "moduleId"); RunicIdentifier.Require(bindingId, "bindingId"); return Unregister(moduleId + "/" + bindingId, (long?)null); } public bool TryGetBinding(string moduleId, string bindingId, out KeybindingDescriptor binding) { binding = null; if (!RunicIdentifier.IsValid(moduleId) || !RunicIdentifier.IsValid(bindingId)) { return false; } lock (_sync) { if (!_byQualifiedId.TryGetValue(moduleId + "/" + bindingId, out var value)) { return false; } binding = value.Binding; return true; } } public IReadOnlyList<KeybindingDescriptor> GetBindings() { lock (_sync) { List<KeybindingDescriptor> list = new List<KeybindingDescriptor>(_byQualifiedId.Count); foreach (BindingEntry value in _byQualifiedId.Values) { list.Add(value.Binding); } list.Sort((KeybindingDescriptor left, KeybindingDescriptor right) => StringComparer.Ordinal.Compare(left.QualifiedId, right.QualifiedId)); return list.AsReadOnly(); } } public IReadOnlyList<KeybindingConflict> GetConflicts() { lock (_sync) { List<KeybindingConflict> list = new List<KeybindingConflict>(); foreach (string key in _byChord.Keys) { KeybindingConflict keybindingConflict = CreateConflictLocked(key); if (keybindingConflict != null) { list.Add(keybindingConflict); } } list.Sort((KeybindingConflict left, KeybindingConflict right) => StringComparer.Ordinal.Compare(left.Chord.CanonicalId, right.Chord.CanonicalId)); return list.AsReadOnly(); } } public IReadOnlyList<KeybindingConflict> GetConflictsFor(string moduleId) { RunicIdentifier.Require(moduleId, "moduleId"); IReadOnlyList<KeybindingConflict> conflicts = GetConflicts(); List<KeybindingConflict> list = new List<KeybindingConflict>(); foreach (KeybindingConflict item in conflicts) { foreach (KeybindingDescriptor binding in item.Bindings) { if (string.Equals(binding.ModuleId, moduleId, StringComparison.Ordinal)) { list.Add(item); break; } } } return list.AsReadOnly(); } internal bool IsActive(string qualifiedId, long token) { lock (_sync) { BindingEntry value; return _byQualifiedId.TryGetValue(qualifiedId, out value) && value.Token == token; } } internal bool Unregister(string qualifiedId, long? requiredToken) { KeybindingDescriptor binding; KeybindingConflict conflict; lock (_sync) { if (!_byQualifiedId.TryGetValue(qualifiedId, out var entry) || (requiredToken.HasValue && entry.Token != requiredToken.Value)) { return false; } binding = entry.Binding; _byQualifiedId.Remove(qualifiedId); List<BindingEntry> list = _byChord[binding.Chord.CanonicalId]; list.RemoveAll((BindingEntry candidate) => candidate.Token == entry.Token); if (list.Count == 0) { _byChord.Remove(binding.Chord.CanonicalId); } conflict = CreateConflictLocked(binding.Chord.CanonicalId); } RaiseChanged(new KeybindingsChangedEventArgs(KeybindingChangeKind.Unregistered, binding, conflict)); return true; } private KeybindingConflict CreateConflictLocked(string chordId) { if (!_byChord.TryGetValue(chordId, out var value) || value.Count < 2) { return null; } List<KeybindingDescriptor> list = new List<KeybindingDescriptor>(value.Count); foreach (BindingEntry item in value) { list.Add(item.Binding); } list.Sort((KeybindingDescriptor left, KeybindingDescriptor right) => StringComparer.Ordinal.Compare(left.QualifiedId, right.QualifiedId)); return new KeybindingConflict(list[0].Chord, list.AsReadOnly()); } private void RaiseChanged(KeybindingsChangedEventArgs arguments) { EventHandler<KeybindingsChangedEventArgs> eventHandler = this.Changed; if (eventHandler == null) { return; } Delegate[] invocationList = eventHandler.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { EventHandler<KeybindingsChangedEventArgs> eventHandler2 = (EventHandler<KeybindingsChangedEventArgs>)invocationList[i]; try { eventHandler2(this, arguments); } catch (Exception) { } } } } public sealed class KeybindingRegistration : IDisposable { private readonly KeybindingConflictRegistry _registry; private readonly long _token; public KeybindingDescriptor Binding { get; } public bool IsActive => _registry.IsActive(Binding.QualifiedId, _token); internal KeybindingRegistration(KeybindingConflictRegistry registry, KeybindingDescriptor binding, long token) { _registry = registry; Binding = binding; _token = token; } public void Dispose() { _registry.Unregister(Binding.QualifiedId, _token); } } } namespace RunicInventory { internal static class Diagnostics { private static ManualLogSource _log; internal static void Initialize(ManualLogSource log) { _log = log; } internal static void Info(string value) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)Bound(value)); } } internal static void Warn(string value) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)Bound(value)); } } internal static void Error(Exception exception, string context) { ManualLogSource log = _log; if (log != null) { log.LogError((object)(Bound(context) + " " + ((exception == null) ? "unknown" : (exception.GetType().Name + ": " + Bound(exception.Message))))); } } internal static void Trace(string value) { ConfigEntry<bool> verboseDiagnostics = InventoryConfig.VerboseDiagnostics; if (verboseDiagnostics != null && verboseDiagnostics.Value) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)Bound(value)); } } } private static string Bound(string value) { string text = value ?? string.Empty; if (text.Length > 512) { return text.Substring(0, 512); } return text; } } internal static class InventoryConfig { internal static ConfigEntry<bool> Enabled { get; private set; } internal static ConfigEntry<bool> ShowInventoryStatus { get; private set; } internal static ConfigEntry<bool> ShowRoleLabels { get; private set; } internal static ConfigEntry<bool> ShowPickupPreview { get; private set; } internal static ConfigEntry<bool> CompactQuiverLayout { get; private set; } internal static ConfigEntry<string> FilteredPickupItems { get; private set; } internal static ConfigEntry<string> SortRows { get; private set; } internal static ConfigEntry<KeyboardShortcut> Quick1 { get; private set; } internal static ConfigEntry<KeyboardShortcut> Quick2 { get; private set; } internal static ConfigEntry<KeyboardShortcut> Quick3 { get; private set; } internal static ConfigEntry<KeyboardShortcut> Sort { get; private set; } internal static ConfigEntry<KeyboardShortcut> ToggleLock { get; private set; } internal static ConfigEntry<bool> ControllerEnabled { get; private set; } internal static ConfigEntry<string> ControllerModifier { get; private set; } internal static ConfigEntry<string> ControllerQuick1 { get; private set; } internal static ConfigEntry<string> ControllerQuick2 { get; private set; } internal static ConfigEntry<string> ControllerQuick3 { get; private set; } internal static ConfigEntry<string> ControllerSort { get; private set; } internal static ConfigEntry<string> ControllerToggleLock { get; private set; } internal static ConfigEntry<bool> VerboseDiagnostics { get; private set; } internal static void Bind(ConfigFile config) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) Enabled = config.Bind<bool>("General", "Enabled", true, new ConfigDescription(RunicText.Get("text_8d4aa0761bf8"), (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes() })); ShowInventoryStatus = config.Bind<bool>("UI", "ShowInventoryStatus", false, RunicText.Get("text_3c4bb869b3e0")); ShowRoleLabels = config.Bind<bool>("UI", "ShowRoleLabels", true, RunicText.Get("text_10dc0db42044")); ShowPickupPreview = config.Bind<bool>("UI", "ShowPickupPreview", true, RunicText.Get("text_9aeb1688941e")); CompactQuiverLayout = config.Bind<bool>("UI", "CompactQuiverLayout", true, RunicText.Get("text_90beaaeae893")); FilteredPickupItems = config.Bind<string>("Pickup Filter", "Items", string.Empty, RunicText.Get("text_c2e4efc127e7")); SortRows = config.Bind<string>("Sort", "Rows", "1,2", RunicText.Get("text_cfd403f31628")); Quick1 = config.Bind<KeyboardShortcut>("Keyboard", "UseQuickSlot1", new KeyboardShortcut((KeyCode)49, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), RunicText.Get("text_c19bc8ec2e08")); Quick2 = config.Bind<KeyboardShortcut>("Keyboard", "UseQuickSlot2", new KeyboardShortcut((KeyCode)50, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), RunicText.Get("text_4a213600b47c")); Quick3 = config.Bind<KeyboardShortcut>("Keyboard", "UseQuickSlot3", new KeyboardShortcut((KeyCode)51, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), RunicText.Get("text_dfce180bc2ee")); Sort = config.Bind<KeyboardShortcut>("Keyboard", "SortSelectedRows", new KeyboardShortcut((KeyCode)105, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), RunicText.Get("text_6c6419e7f4d1")); ToggleLock = config.Bind<KeyboardShortcut>("Keyboard", "ToggleFocusedSlotLock", new KeyboardShortcut((KeyCode)108, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), RunicText.Get("text_31d0dc2a5d57")); ControllerEnabled = config.Bind<bool>("Controller", "Enabled", true, RunicText.Get("text_d760c9e83479")); ControllerModifier = config.Bind<string>("Controller", "ModifierAction", "JoyAltKeys", RunicText.Get("text_0040e54c0301")); ControllerQuick1 = config.Bind<string>("Controller", "UseQuickSlot1Action", "JoyMap", RunicText.Get("text_21358e79d50b")); ControllerQuick2 = config.Bind<string>("Controller", "UseQuickSlot2Action", "JoyButtonY", RunicText.Get("text_78efbbf396ce")); ControllerQuick3 = config.Bind<string>("Controller", "UseQuickSlot3Action", "JoyRBumper", RunicText.Get("text_d88ee291d981")); ControllerSort = config.Bind<string>("Controller", "SortSelectedRowsAction", "JoyButtonA", RunicText.Get("text_454da9489f48")); ControllerToggleLock = config.Bind<string>("Controller", "ToggleFocusedSlotLockAction", "JoyButtonB", RunicText.Get("text_cece443edf13")); VerboseDiagnostics = config.Bind<bool>("Diagnostics", "Verbose", false, RunicText.Get("text_00bba4d37a51")); } } [BepInPlugin("chazman.RunicInventory", "Runic Inventory", "1.1.10")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "chazman.RunicInventory"; public const string Name = "Runic Inventory"; public const string Version = "1.1.10"; public const string ModuleId = "runic.inventory"; public const string ProtocolVersion = "1.0"; private readonly List<KeybindingRegistration> _bindings = new List<KeybindingRegistration>(); private readonly KeybindingConflictRegistry _keybindings = new KeybindingConflictRegistry(); private Harmony _harmony; private InventoryRuntime _runtime; private bool _configurationSubscribed; private bool _inputLayoutSubscribed; private bool _shuttingDown; internal static Plugin Instance { get; private set; } internal static bool RuntimeReady { get; private set; } internal InventoryRuntime Runtime => _runtime; private void Awake() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown Instance = this; Diagnostics.Initialize(((BaseUnityPlugin)this).Logger); InventoryIntegrationApi.BeginInitialization(); try { InventoryConfig.Bind(((BaseUnityPlugin)this).Config); ((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged; _configurationSubscribed = true; ZInput.OnInputLayoutChanged += OnInputLayoutChanged; _inputLayoutSubscribed = true; if (!ValheimContracts.Initialize(out var problem)) { throw new MissingMethodException(problem); } _runtime = new InventoryRuntime(batch: false); _harmony = new Harmony("chazman.RunicInventory"); _harmony.PatchAll(typeof(Plugin).Assembly); BetterArcheryCompatibility.Initialize(_harmony); RegisterBindings(); _runtime.Initialize(); InventoryIntegrationApi.Attach(_runtime); RuntimeReady = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Runic Inventory v1.1.10 ready. Native owner-local topology, locks, sort, quick use, equipment relocation, saves, and tombstones remain Valheim-owned."); if (Application.isBatchMode) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Batch transport detected: only an exact owning local Player can activate Inventory; a true dedicated server remains inert."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Runic Inventory startup failed: " + ex)); ShutdownRuntime(); InventoryIntegrationApi.MarkStartupFailed(); } } private void Update() { if (!RuntimeReady || _runtime == null) { return; } try { _runtime.Tick(); } catch (Exception exception) { Diagnostics.Error(exception, RunicText.Get("text_6586951ebeea")); _runtime.FailClosed("runtime.exception"); } } private void OnGUI() { if (!RuntimeReady || _runtime == null) { return; } try { _runtime.Draw(); } catch (Exception exception) { Diagnostics.Error(exception, RunicText.Get("text_634f4fd622aa")); } } private void OnSettingChanged(object sender, SettingChangedEventArgs arguments) { if (!RuntimeReady || _runtime == null || (object)((arguments != null) ? arguments.ChangedSetting : null) == InventoryConfig.CompactQuiverLayout) { return; } try { _runtime.OnConfigurationChanged(); } catch (Exception exception) { Diagnostics.Error(exception, RunicText.Get("text_c0e41f8a2d26")); _runtime.FailClosed("config.refresh-failed"); } try { RefreshBindings(); } catch (Exception exception2) { Diagnostics.Error(exception2, RunicText.Get("text_7c7aad401442")); } } private void OnInputLayoutChanged() { ControllerChordSession.Reset(); Diagnostics.Trace("Controller input layout changed; Inventory bindings will be re-resolved."); } private void OnDestroy() { ShutdownRuntime(); Diagnostics.Initialize(null); Instance = null; } private void RegisterBindings() { //IL_0024: 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_0062: 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_00a0: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> enabled = InventoryConfig.Enabled; if (enabled != null && enabled.Value) { RegisterKeyboard("quick-1", "Use quick slot 1", InventoryConfig.Quick1.Value, "gameplay"); RegisterKeyboard("quick-2", "Use quick slot 2", InventoryConfig.Quick2.Value, "gameplay"); RegisterKeyboard("quick-3", "Use quick slot 3", InventoryConfig.Quick3.Value, "gameplay"); RegisterKeyboard("sort", "Sort selected safe inventory rows", InventoryConfig.Sort.Value, "inventory"); RegisterKeyboard("lock", "Toggle focused inventory slot lock", InventoryConfig.ToggleLock.Value, "inventory"); ConfigEntry<bool> controllerEnabled = InventoryConfig.ControllerEnabled; if (controllerEnabled != null && controllerEnabled.Value) { string modifier = BoundControllerAction(InventoryConfig.ControllerModifier.Value); string quick = BoundControllerAction(InventoryConfig.ControllerQuick1.Value); string text = BoundControllerAction(InventoryConfig.ControllerQuick2.Value); string text2 = BoundControllerAction(InventoryConfig.ControllerQuick3.Value); string text3 = BoundControllerAction(InventoryConfig.ControllerSort.Value); string text4 = BoundControllerAction(InventoryConfig.ControllerToggleLock.Value); quick = ControllerBindingPolicy.EffectiveQuick1Action(modifier, quick, text, text2, text3, text4, out var _); RegisterController("controller-quick-1", "Use quick slot 1", quick, modifier, "gameplay"); RegisterController("controller-quick-2", "Use quick slot 2", text, modifier, "gameplay"); RegisterController("controller-quick-3", "Use quick slot 3", text2, modifier, "gameplay"); RegisterController("controller-sort", "Sort selected safe inventory rows", text3, modifier, "inventory"); RegisterController("controller-lock", "Toggle focused inventory slot lock", text4, modifier, "inventory"); } } } private void RefreshBindings() { DisposeBindings(); try { RegisterBindings(); } catch { DisposeBindings(); throw; } } private void DisposeBindings() { for (int num = _bindings.Count - 1; num >= 0; num--) { try { _bindings[num].Dispose(); } catch (Exception exception) { Diagnostics.Error(exception, RunicText.Get("text_bee5ee3f3cbd")); } } _bindings.Clear(); } private unsafe void RegisterKeyboard(string id, string display, KeyboardShortcut shortcut, string context) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return; } List<string> list = new List<string>(); foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (list.Count >= 8) { throw new InvalidOperationException("A keyboard chord may contain at most eight modifiers."); } list.Add(((object)(*(KeyCode*)(&modifier))/*cast due to .constrained prefix*/).ToString()); } _bindings.Add(_keybindings.Register(new KeybindingDescriptor("runic.inventory", id, display, new InputChord("keyboard", ((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString(), list), context))); } private void RegisterController(string id, string display, string action, string modifier, string context) { if (action.Length == 0 || modifier.Length == 0) { return; } try { _bindings.Add(_keybindings.Register(new KeybindingDescriptor("runic.inventory", id, display, new InputChord("controller", action, new string[1] { modifier }), context))); } catch (ArgumentException) { Diagnostics.Warn("Inventory skipped invalid controller route: " + id + "."); } } private static string BoundControllerAction(string value) { string text = value ?? string.Empty; if (text.Length == 0 || text.Length > 64) { return string.Empty; } string text2 = text.Trim(); if (!text2.StartsWith("Joy", StringComparison.Ordinal)) { return string.Empty; } for (int i = 0; i < text2.Length; i++) { if (!char.IsLetterOrDigit(text2[i]) && text2[i] != '_' && text2[i] != '-') { return string.Empty; } } return text2; } private void ShutdownRuntime() { if (_shuttingDown) { return; } _shuttingDown = true; RuntimeReady = false; InventoryRuntime runtime = _runtime; InventoryIntegrationApi.Detach(runtime); if (_configurationSubscribed) { ((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged; _configurationSubscribed = false; } if (_inputLayoutSubscribed) { ZInput.OnInputLayoutChanged -= OnInputLayoutChanged; _inputLayoutSubscribed = false; } ControllerChordSession.Reset(); DisposeBindings(); try { runtime?.Dispose(); } catch (Exception exception) { Diagnostics.Error(exception, RunicText.Get("text_bf7b364b87ab")); } _runtime = null; try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception exception2) { Diagnostics.Error(exception2, RunicText.Get("text_749140dc00e4")); } _harmony = null; BetterArcheryCompatibility.Reset(); } } } namespace RunicInventory.Integration { internal static class BetterArcheryCompatibility { internal const string Guid = "ishid4.mods.betterarchery"; internal const string AuditedHash = "549B3B6AC69C512E4B3BA1EEECD34687ACB72712A3B068BD1D2783B06BE1F823"; private static ConfigEntry<bool> _enabled; private static FieldInfo _row; private static MethodInfo _equipped; private static MethodInfo _getElement; private static MethodInfo _binding; private static ConfigEntry<KeyboardShortcut> _holdingKey; private static FieldInfo _hasAuga; internal static bool Active { get { ConfigEntry<bool> enabled = _enabled; if (enabled != null && enabled.Value) { return _row != null; } return false; } } internal static int QuiverRow { get { if (!Active) { return -1; } return (int)_row.GetValue(null); } } internal static bool Equipped { get { if (Active) { return (bool)_equipped.Invoke(null, null); } return false; } } internal static void Initialize(Harmony harmony) { //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Expected O, but got Unknown //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Expected O, but got Unknown //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Expected O, but got Unknown //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Expected O, but got Unknown //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Expected O, but got Unknown //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Expected O, but got Unknown //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: Expected O, but got Unknown if (!Chainloader.PluginInfos.TryGetValue("ishid4.mods.betterarchery", out var value) || (Object)(object)value.Instance == (Object)null) { return; } Assembly assembly = ((object)value.Instance).GetType().Assembly; using (FileStream inputStream = File.OpenRead(assembly.Location)) { using SHA256 sHA = SHA256.Create(); if (BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "") != "549B3B6AC69C512E4B3BA1EEECD34687ACB72712A3B068BD1D2783B06BE1F823") { Diagnostics.Warn("Better Archery binary is not the audited quiver build; automatic layout integration was not enabled."); return; } } Type type = assembly.GetType("BetterArchery.BetterArchery", throwOnError: true); FieldInfo fieldInfo = AccessTools.Field(type, "QuiverRowIndex"); ConfigEntry<bool> val = AccessTools.Field(type, "ConfigQuiverEnabled")?.GetValue(null) as ConfigEntry<bool>; MethodInfo methodInfo = AccessTools.Method(type, "IsQuiverEquipped", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(type, "GetBindingKeycode", new Type[1] { typeof(int) }, (Type[])null); ConfigEntry<KeyboardShortcut> val2 = AccessTools.Field(type, "HoldingKeyCode")?.GetValue(null) as ConfigEntry<KeyboardShortcut>; MethodInfo methodInfo3 = AccessTools.Method(assembly.GetType("BetterArchery.InventoryGrid_UpdateGui_Patch", throwOnError: true), "HideModRows", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(assembly.GetType("BetterArchery.InventoryGrid_UpdateGui_Patch", throwOnError: true), "Postfix", (Type[])null, (Type[])null); MethodInfo methodInfo5 = AccessTools.Method(assembly.GetType("BetterArchery.Tombstone+TombStone_OnTakeAllSuccess_Patch", throwOnError: true), "Postfix", (Type[])null, (Type[])null); MethodInfo methodInfo6 = AccessTools.Method(assembly.GetType("BetterArchery.TerminalAwake_Patch+<>c", throwOnError: true), "<Postfix>b__0_0", (Type[])null, (Type[])null); MethodInfo methodInfo7 = AccessTools.Method(assembly.GetType("BetterArchery.Player_SetInventorySize_Patch", throwOnError: true), "Prefix", (Type[])null, (Type[])null); MethodInfo methodInfo8 = AccessTools.Method(assembly.GetType("BetterArchery.Inventory_FindEmptySlot_Patch", throwOnError: true), "Prefix", (Type[])null, (Type[])null); MethodInfo methodInfo9 = AccessTools.Method(assembly.GetType("BetterArchery.Inventory_HaveEmptySlot_Patch", throwOnError: true), "Prefix", (Type[])null, (Type[])null); MethodInfo methodInfo10 = AccessTools.Method(typeof(InventoryGrid), "GetElement", new Type[3] { typeof(int), typeof(int), typeof(int) }, (Type[])null); if (fieldInfo?.FieldType != typeof(int) || val == null || methodInfo?.ReturnType != typeof(bool) || methodInfo3 == null || methodInfo5 == null || methodInfo6 == null || methodInfo7 == null || methodInfo8 == null || methodInfo9 == null || methodInfo10 == null || methodInfo2?.ReturnType != typeof(KeyCode) || val2 == null) { throw new MissingMemberException("Better Archery quiver contract changed."); } harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(BetterArcheryCompatibility), "HideRows", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(BetterArcheryCompatibility), "AllowLegacyRecovery", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo6, new HarmonyMethod(typeof(BetterArcheryCompatibility), "AllowDebugCommand", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo7, new HarmonyMethod(typeof(BetterArcheryCompatibility), "AllowArcheryResize", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo8, new HarmonyMethod(typeof(BetterArcheryCompatibility), "AllowArcheryFind", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo9, new HarmonyMethod(typeof(BetterArcheryCompatibility), "AllowArcheryCapacity", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); try { if (methodInfo4 != null) { harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(BetterArcheryCompatibility), "CompactPanel", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception exception) { CompactQuiverPanel.Fault(exception); } _hasAuga = AccessTools.Field(type, "HasAuga"); _row = fieldInfo; _equipped = methodInfo; _getElement = methodInfo10; _enabled = val; _binding = methodInfo2; _holdingKey = val2; Diagnostics.Info("Better Archery quiver compatibility ready with HarmonyX resize/slot guards. Quiver and Runic roles use separate reserved rows. Configure distinct quiver/quick-slot hotkeys if needed."); } private static bool AllowArcheryResize(Player __0, ref bool __result) { if (Plugin.Instance?.Runtime?.HandlesNativeInventorySize(__0) != true) { return true; } __result = false; return false; } private static bool AllowArcheryFind(Inventory __0, ref Vector2i __1, bool __2, ref bool __result) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; if (inventoryRuntime == null || !inventoryRuntime.TryFindEmptySlot(__0, __2, out var result)) { return true; } __1 = result; __result = false; return false; } private static bool AllowArcheryCapacity(Inventory __0, ref bool __1, ref bool __result) { //IL_0024: 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) InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; if (inventoryRuntime == null || !inventoryRuntime.TryFindEmptySlot(__0, topFirst: true, out var result)) { return true; } __1 = result.x >= 0 && result.y >= 0; __result = false; return false; } internal static void SetQuiverRow(int row) { if (Active) { _row.SetValue(null, row); } } internal static bool QuiverShortcutDown() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (!Equipped) { return false; } KeyboardShortcut value = _holdingKey.Value; KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey; if ((int)mainKey != 0 && !Input.GetKey(mainKey)) { return false; } for (int i = 0; i < 3; i++) { if (Input.GetKeyDown((KeyCode)_binding.Invoke(null, new object[1] { i }))) { return true; } } return false; } internal static void Reset() { CompactQuiverPanel.Restore(); _enabled = null; _holdingKey = null; _row = null; _equipped = (_getElement = (_binding = null)); _hasAuga = null; } private static void CompactPanel(InventoryGrid __0) { try { if ((Object)(object)__0 == (Object)null || ((Object)__0).name != "PlayerGrid") { return; } if (!OwnsLayout(__0.GetInventory()) || __0.GetInventory().GetHeight() != QuiverRow + 2 || (bool?)_hasAuga?.GetValue(null) == true) { CompactQuiverPanel.Restore(); return; } CompactQuiverPanel.Apply(__0, delegate(int x, int y) { object? obj = _getElement.Invoke(__0, new object[3] { x, y, 8 }); return (Component)((obj is Component) ? obj : null); }, Equipped); } catch (Exception exception) { CompactQuiverPanel.Fault(exception); } } private static bool OwnsLayout(Inventory inventory) { if (Active && (Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).GetInventory() == inventory) { Dictionary<string, string> customData = Player.m_localPlayer.m_customData; if (customData != null && customData.ContainsKey("runic.inventory.betterarchery.v1")) { return Player.m_localPlayer.m_customData.ContainsKey("runic.inventory.extra-row.v1"); } } return false; } private static bool HideRows(InventoryGrid __0, IList __1) { Inventory val = ((__0 != null) ? __0.GetInventory() : null); if (!OwnsLayout(val)) { return true; } int quiverRow = QuiverRow; if (quiverRow < 5 || quiverRow + 1 != val.GetHeight() - 1) { return false; } bool equipped = Equipped; for (int i = quiverRow - 1; i <= quiverRow + 1; i++) { for (int j = 0; j < 8; j++) { object? obj = _getElement.Invoke(__0, new object[3] { j, i, val.GetWidth() }); Component val2 = (Component)((obj is Component) ? obj : null); if ((Object)(object)val2 != (Object)null) { val2.gameObject.SetActive(i == quiverRow + 1 || (i == quiverRow && j < 3 && equipped)); } } } return false; } private static bool AllowLegacyRecovery() { Player localPlayer = Player.m_localPlayer; return !OwnsLayout((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); } private static bool AllowDebugCommand(ConsoleEventArgs __0) { Player localPlayer = Player.m_localPlayer; if (!OwnsLayout((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null) || __0.Length < 2 || __0.FullLine.Substring(__0[0].Length + 1) != "drop") { return true; } __0.Context.AddString("Runic Inventory: ba drop assumes extra rows are invalid. Move items manually while the combined quiver layout is active."); return false; } } internal static class CompactQuiverPanel { private static readonly RectTransform[] Cells = (RectTransform[])(object)new RectTransform[8]; private static FieldInfo _baseHeight; private static FieldInfo _rowHeight; private static RectTransform _panel; private static float _originalY; private static float _appliedY; private static float _originalHeight; private static float _appliedHeight; private static bool _warned; internal static void Apply(InventoryGrid grid, Func<int, int, Component> element, bool equipped) { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = grid.GetInventory(); InventoryGui instance = InventoryGui.instance; if (inventory == null || (Object)(object)instance == (Object)null || (Object)(object)instance.m_player == (Object)null) { Restore(); return; } Component obj = element(0, 0); Transform obj2 = ((obj != null) ? obj.transform : null); RectTransform val = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null); if ((Object)(object)val == (Object)null) { Restore(); return; } int num = inventory.GetHeight() - 1; float? num2 = null; if (equipped) { for (int i = 0; i < 3; i++) { Component obj3 = element(i, num - 1); Transform obj4 = ((obj3 != null) ? obj3.transform : null); RectTransform val2 = (RectTransform)(object)((obj4 is RectTransform) ? obj4 : null); if ((Object)(object)val2 == (Object)null || !((Component)val2).gameObject.activeSelf || (Object)(object)((Transform)val2).parent != (Object)(object)((Transform)val).parent) { Restore(); return; } num2 = Math.Min(num2 ?? val2.anchoredPosition.y, val2.anchoredPosition.y); } } if (!QuiverPanelLayout.TryPlan(num - 2, grid.m_elementSpace, val.anchoredPosition.y, num2, InventoryConfig.CompactQuiverLayout.Value, out var equipmentY, out var removedHeight)) { Restore(); return; } _baseHeight = _baseHeight ?? AccessTools.Field(typeof(InventoryGui), "m_playerHeight"); _rowHeight = _rowHeight ?? AccessTools.Field(typeof(InventoryGui), "m_invGridHeight"); float num3 = (float)_baseHeight.GetValue(instance) + (float)(inventory.GetHeight() - 4) * (float)_rowHeight.GetValue(instance); if (num3 <= removedHeight || float.IsNaN(num3) || float.IsInfinity(num3)) { Restore(); return; } for (int j = 0; j < 8; j++) { Component obj5 = element(j, num); Transform obj6 = ((obj5 != null) ? obj5.transform : null); RectTransform val3 = (RectTransform)(object)((obj6 is RectTransform) ? obj6 : null); if ((Object)(object)val3 == (Object)null || (Object)(object)((Transform)val3).parent != (Object)(object)((Transform)val).parent) { Restore(); return; } } _originalY = val.anchoredPosition.y - (float)num * grid.m_elementSpace; _appliedY = equipmentY; for (int k = 0; k < 8; k++) { ref RectTransform reference = ref Cells[k]; Transform transform = element(k, num).transform; reference = (RectTransform)(object)((transform is RectTransform) ? transform : null); Vector2 anchoredPosition = Cells[k].anchoredPosition; anchoredPosition.y = equipmentY; if (Cells[k].anchoredPosition != anchoredPosition) { Cells[k].anchoredPosition = anchoredPosition; } } _panel = instance.m_player; _originalHeight = num3; _appliedHeight = num3 - removedHeight; Vector2 sizeDelta = _panel.sizeDelta; sizeDelta.y = _appliedHeight; if (_panel.sizeDelta != sizeDelta) { _panel.sizeDelta = sizeDelta; } } internal static void Fault(Exception exception) { Restore(); if (!_warned) { _warned = true; Diagnostics.Error(exception, RunicText.Get("text_2e2ca90ffe5a")); } } internal static void Restore() { //IL_0018: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) RectTransform[] cells = Cells; foreach (RectTransform val in cells) { if ((Object)(object)val != (Object)null && Mathf.Approximately(val.anchoredPosition.y, _appliedY)) { Vector2 anchoredPosition = val.anchoredPosition; anchoredPosition.y = _originalY; val.anchoredPosition = anchoredPosition; } } if ((Object)(object)_panel != (Object)null && Mathf.Approximately(_panel.sizeDelta.y, _appliedHeight)) { Vector2 sizeDelta = _panel.sizeDelta; sizeDelta.y = _originalHeight; _panel.sizeDelta = sizeDelta; } Array.Clear(Cells, 0, Cells.Length); _panel = null; } } internal sealed class ControllerBindingState { internal const int DefinitionCount = 6; internal ButtonDef Modifier { get; } internal ButtonDef Quick1 { get; } internal ButtonDef Quick2 { get; } internal ButtonDef Quick3 { get; } internal ButtonDef Sort { get; } internal ButtonDef ToggleLock { get; } internal string ReasonCode { get; } internal bool LegacyQuick1Mapped { get; } internal int ValidRouteCount { get; } internal bool Ready { get { if (Modifier != null) { return ValidRouteCount > 0; } return false; } } internal bool AllValid { get { if (Modifier != null) { return ValidRouteCount == 5; } return false; } } internal ControllerBindingState(ButtonDef modifier, ButtonDef quick1, ButtonDef quick2, ButtonDef quick3, ButtonDef sort, ButtonDef toggleLock, string reasonCode, bool legacyQuick1Mapped) { Modifier = modifier; Quick1 = quick1; Quick2 = quick2; Quick3 = quick3; Sort = sort; ToggleLock = toggleLock; ReasonCode = reasonCode ?? "controller.invalid"; LegacyQuick1Mapped = legacyQuick1Mapped; ValidRouteCount = ((quick1 != null) ? 1 : 0) + ((quick2 != null) ? 1 : 0) + ((quick3 != null) ? 1 : 0) + ((sort != null) ? 1 : 0) + ((toggleLock != null) ? 1 : 0); } internal ButtonDef Definition(int index) { return (ButtonDef)(index switch { 0 => Modifier, 1 => Quick1, 2 => Quick2, 3 => Quick3, 4 => Sort, 5 => ToggleLock, _ => throw new ArgumentOutOfRangeException("index"), }); } } internal static class ControllerBindings { private static ZInput _instance; private static ControllerBindingState _cached; internal static ControllerBindingState Resolve() { if (_cached != null && _instance == ZInput.instance) { return _cached; } string text = Name(InventoryConfig.ControllerModifier?.Value); string quick = Name(InventoryConfig.ControllerQuick1?.Value); string text2 = Name(InventoryConfig.ControllerQuick2?.Value); string text3 = Name(InventoryConfig.ControllerQuick3?.Value); string text4 = Name(InventoryConfig.ControllerSort?.Value); string text5 = Name(InventoryConfig.ControllerToggleLock?.Value); quick = ControllerBindingPolicy.EffectiveQuick1Action(text, quick, text2, text3, text4, text5, out var legacyMapped); _instance = ZInput.instance; if (_instance == null) { return _cached = new ControllerBindingState(null, null, null, null, null, null, "controller.zinput-unavailable", legacyMapped); } string path; ButtonDef val = ResolveAction(text, out path); string[] array = new string[5] { quick, text2, text3, text4, text5 }; string[] array2 = new string[5]; ButtonDef[] array3 = (ButtonDef[])(object)new ButtonDef[5]; for (int i = 0; i < 5; i++) { array3[i] = ResolveAction(array[i], out array2[i]); } int num = ControllerBindingPolicy.ValidRouteMask(text, path, array, array2); for (int j = 0; j < 5; j++) { if (!ControllerBindingPolicy.RouteIsValid(num, j)) { array3[j] = null; } } string reasonCode = ((val == null) ? "controller.modifier-invalid" : (num switch { 0 => "controller.no-valid-routes", 31 => "ok", _ => "controller.one-or-more-routes-invalid", })); return _cached = new ControllerBindingState(val, array3[0], array3[1], array3[2], array3[3], array3[4], reasonCode, legacyMapped); } internal static void Invalidate() { _instance = null; _cached = null; } internal static bool ShouldReserveAction(string action) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Invalid comparison between Unknown and I4 ConfigEntry<bool> controllerEnabled = InventoryConfig.ControllerEnabled; if (controllerEnabled == null || !controllerEnabled.Value || string.IsNullOrEmpty(action) || ZInput.instance == null) { return false; } ControllerBindingState controllerBindingState = Resolve(); if (controllerBindingState.Ready && controllerBindingState.Modifier.Held) { ButtonDef quick = controllerBindingState.Quick1; if (quick == null || !quick.Pressed) { ButtonDef quick2 = controllerBindingState.Quick2; if (quick2 == null || !quick2.Pressed) { ButtonDef quick3 = controllerBindingState.Quick3; if (quick3 == null || !quick3.Pressed) { ButtonDef sort = controllerBindingState.Sort; if (sort == null || !sort.Pressed) { ButtonDef toggleLock = controllerBindingState.ToggleLock; if (toggleLock == null || !toggleLock.Pressed) { goto IL_00a3; } } } } } ButtonDef buttonDef; try { buttonDef = ZInput.instance.GetButtonDef(action); } catch (Exception) { return false; } if (buttonDef == null || (int)buttonDef.Source != 180) { return false; } string actionPath; try { actionPath = buttonDef.GetActionPath(true); } catch (Exception) { return false; } if (!SamePath(actionPath, controllerBindingState.Modifier) && !SamePath(actionPath, controllerBindingState.Quick1) && !SamePath(actionPath, controllerBindingState.Quick2) && !SamePath(actionPath, controllerBindingState.Quick3) && !SamePath(actionPath, controllerBindingState.Sort)) { return SamePath(actionPath, controllerBindingState.ToggleLock); } return true; } goto IL_00a3; IL_00a3: return false; } private static ButtonDef ResolveAction(string action, out string path) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 path = string.Empty; if (action.Length == 0 || action.Length > 64 || !action.StartsWith("Joy", StringComparison.Ordinal)) { return null; } ButtonDef buttonDef; try { buttonDef = ZInput.instance.GetButtonDef(action); } catch (Exception) { return null; } if (buttonDef == null || (int)buttonDef.Source != 180) { return null; } try { path = buttonDef.GetActionPath(true)?.Trim() ?? string.Empty; } catch (Exception) { return null; } if (path.Length != 0) { return buttonDef; } return null; } private static string Name(string value) { string text = value ?? string.Empty; if (text.Length == 0 || text.Length > 64) { return string.Empty; } string text2 = text.Trim(); for (int i = 0; i < text2.Length; i++) { if (!char.IsLetterOrDigit(text2[i]) && text2[i] != '_' && text2[i] != '-') { return string.Empty; } } return text2; } private static bool SamePath(string path, ButtonDef definition) { if (string.IsNullOrEmpty(path) || definition == null) { return false; } try { return string.Equals(path, definition.GetActionPath(true), StringComparison.Ordinal); } catch (Exception) { return false; } } } internal static class KeyboardInput { private static readonly KeyCode[] ModifierKeys; internal static bool ShortcutDown(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false)) { return false; } int num = 0; int num2 = 0; foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (++num2 > 8) { return false; } if ((int)modifier == 0 || !ZInput.GetKey(modifier, false)) { return false; } int num3 = ModifierIndex(modifier); if (num3 >= 0) { num |= 1 << num3; } } for (int i = 0; i < ModifierKeys.Length; i++) { if ((num & (1 << i)) == 0 && ZInput.GetKey(ModifierKeys[i], false)) { return false; } } return true; } internal static bool ShouldReserveVanillaAction(string action) { //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) KeyCode val = (KeyCode)(action switch { "Hotbar1" => 49, "Hotbar2" => 50, "Hotbar3" => 51, "Hotbar4" => 52, "Hotbar5" => 53, "Hotbar6" => 54, "Hotbar7" => 55, "Hotbar8" => 56, _ => 0, }); if ((int)val == 0) { return false; } if (!IsChordFor(val, InventoryConfig.Quick1.Value) && !IsChordFor(val, InventoryConfig.Quick2.Value)) { return IsChordFor(val, InventoryConfig.Quick3.Value); } return true; } private static bool IsChordFor(KeyCode key, KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (((KeyboardShortcut)(ref shortcut)).MainKey == key) { return ShortcutDown(shortcut); } return false; } private static int ModifierIndex(KeyCode value) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between I4 and Unknown for (int i = 0; i < ModifierKeys.Length; i++) { if ((int)ModifierKeys[i] == (int)value) { return i; } } return -1; } static KeyboardInput() { KeyCode[] array = new KeyCode[8]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); ModifierKeys = (KeyCode[])(object)array; } } internal static class InputReservation { internal static bool ShouldSuppress(string action) { InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; if (!Plugin.RuntimeReady || inventoryRuntime == null || !inventoryRuntime.AcceptsInput) { return false; } if (!ControllerChordSession.ShouldSuppress(action) && !ControllerBindings.ShouldReserveAction(action)) { return KeyboardInput.ShouldReserveVanillaAction(action); } return true; } } internal static class ControllerInput { private static ControllerBindingState _loggedBindings; internal static void Tick(InventoryRuntime runtime, bool inventoryVisible, bool gameplayInput) { if (runtime == null) { return; } ConfigEntry<bool> controllerEnabled = InventoryConfig.ControllerEnabled; if (controllerEnabled == null || !controllerEnabled.Value || ZInput.instance == null) { return; } ControllerBindingState controllerBindingState = ControllerBindings.Resolve(); if (_loggedBindings != controllerBindingState) { _loggedBindings = controllerBindingState; if (controllerBindingState.AllValid) { Diagnostics.Info(controllerBindingState.LegacyQuick1Mapped ? "Inventory controller chords validated by six unique effective gamepad paths; the exact legacy default Quick 1 route is using JoyMap." : "Inventory controller chords validated by six unique effective gamepad paths."); } else if (controllerBindingState.Ready) { Diagnostics.Warn("Inventory controller chords partially available (" + controllerBindingState.ValidRouteCount + "/5 routes): " + controllerBindingState.ReasonCode + "."); } else { Diagnostics.Warn("Inventory controller chords disabled: " + controllerBindingState.ReasonCode + "."); } } bool active = ControllerChordSession.Active; if (!controllerBindingState.Ready || active || !controllerBindingState.Modifier.Held) { return; } if (inventoryVisible) { ButtonDef toggleLock = controllerBindingState.ToggleLock; if (toggleLock != null && toggleLock.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.ToggleFocusedLock("controller"); return; } } if (inventoryVisible) { ButtonDef sort = controllerBindingState.Sort; if (sort != null && sort.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.SortSelectedRows("controller"); return; } } if (!gameplayInput) { return; } ButtonDef quick = controllerBindingState.Quick1; if (quick != null && quick.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.UseQuick(InventoryRoleKind.Quick1, "controller"); return; } ButtonDef quick2 = controllerBindingState.Quick2; if (quick2 != null && quick2.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.UseQuick(InventoryRoleKind.Quick2, "controller"); return; } ButtonDef quick3 = controllerBindingState.Quick3; if (quick3 != null && quick3.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.UseQuick(InventoryRoleKind.Quick3, "controller"); } } internal static void ResetLog() { _loggedBindings = null; } } internal static class ControllerChordSession { private static ControllerBindingState _bindings; private static readonly HashSet<string> Paths = new HashSet<string>(StringComparer.Ordinal); private static int _releasedFrame = -1; internal static bool Active { get { UpdateRelease(); return _bindings != null; } } internal static void Begin(ControllerBindingState bindings) { if (bindings == null || !bindings.Ready || _bindings != null) { return; } _bindings = bindings; Paths.Clear(); for (int i = 0; i < 6; i++) { ButtonDef obj = bindings.Definition(i); string text = ((obj != null) ? obj.GetActionPath(true) : null); if (!string.IsNullOrEmpty(text)) { Paths.Add(text); } } _releasedFrame = -1; } internal static bool ShouldSuppress(string action) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 UpdateRelease(); if (_bindings == null || ZInput.instance == null || string.IsNullOrEmpty(action)) { return false; } ButtonDef buttonDef; try { buttonDef = ZInput.instance.GetButtonDef(action); } catch (Exception) { return false; } if (buttonDef == null || (int)buttonDef.Source != 180) { return false; } string actionPath; try { actionPath = buttonDef.GetActionPath(true); } catch (Exception) { return false; } if (!string.IsNullOrEmpty(actionPath)) { return Paths.Contains(actionPath); } return false; } internal static void Reset() { _bindings = null; Paths.Clear(); _releasedFrame = -1; ControllerBindings.Invalidate(); ControllerInput.ResetLog(); } private static void UpdateRelease() { if (_bindings == null) { return; } bool flag = false; for (int i = 0; i < 6; i++) { ButtonDef obj = _bindings.Definition(i); if (obj != null && obj.Held) { flag = true; break; } } if (flag) { _releasedFrame = -1; } else if (_releasedFrame < 0) { _releasedFrame = Time.frameCount; } else if (_releasedFrame != Time.frameCount) { _bindings = null; Paths.Clear(); _releasedFrame = -1; } } } internal sealed class InventoryRuntime : IInventoryTopologyService, IInventoryProtectionService, IInventoryStatusService, IItemProtectionQuery, IDisposable { private sealed class MutationScope : IDisposable { private InventoryRuntime _owner; internal MutationScope(InventoryRuntime owner) { _owner = owner; } public void Dispose() { InventoryRuntime inventoryRuntime = Interlocked.Exchange(ref _owner, null); if (inventoryRuntime != null) { Interlocked.Exchange(ref inventoryRuntime._mutationActive, 0); } } } private bool _rowMigration; private bool _restoringEnabled; private const int MaximumProtectionDiagnostics = 32; private readonly bool _batch; private readonly int _mainThreadId; private readonly Dictionary<StackCapacityKey, int> _stackCapacity = new Dictionary<StackCapacityKey, int>(); private readonly HashSet<string> _protectionDiagnostics = new HashSet<string>(StringComparer.Ordinal); private readonly object _protectionDiagnosticGate = new object(); private Player _player; private Inventory _inventory; private TopologyLayout _layout; private PersistedTopologyState _persisted; private InventoryTopologySnapshot _snapshot; private PickupFilterSet _filters; private InventoryAuthorityMode _mode; private string _reasonCode = "runtime.not-initialized"; private string _statusText = "Runic Inventory: waiting for the local player."; private long _generation; private int _freePickupSlots; private float _cachedWeight; private bool _topologyActive; private bool _playerLoadInProgress; private bool _loadMetadataRefreshed; private int _equipmentTransitionDepth; private int _repairAllowanceDepth; private int _mutationActive; private bool _equipmentRefreshPending; private bool _equipmentTransitionFaulted; private bool _disableCleanupPending; private bool _rebuilding; private bool _disposed; private float _nextMessageTime; private readonly Rect[] _roleSlotRects = (Rect[])(object)new Rect[8]; private bool HasQuiverLayout { get { if (BetterArcheryCompatibility.Active && _player?.m_customData != null && _player.m_customData.ContainsKey("runic.inventory.betterarchery.v1")) { return _player.m_customData.ContainsKey("runic.inventory.extra-row.v1"); } return false; } } private static string[] RoleLabels => new string[8] { RunicText.Get("text_a47fdf6415fc"), RunicText.Get("text_378d83808237"), RunicText.Get("text_38557594d0fc"), RunicText.Get("text_4f646b9c50e7"), RunicText.Get("text_9450d833fa77"), RunicText.Get("text_e7f59d22563b"), RunicText.Get("text_cb419c95c437"), RunicText.Get("text_6cdf4136acbe") }; public string ProviderId => "runic.inventory"; internal bool TopologyActive { get { if (_topologyActive && !_disposed && !_playerLoadInProgress && _mode == InventoryAuthorityMode.AuthoritativeLocal && IsAuthoritativeLocal(_player)) { return LiveDimensionsMatch(); } return false; } } internal InventoryAuthorityMode Mode => _mode; internal bool AcceptsInput => CanEnforceLocks(); internal bool CanDisable(out string message) { message = string.Empty; if (Object.op_Implicit((Object)(object)_player) && (Object)(object)_player == (Object)(object)Player.m_localPlayer && _playerLoadInProgress) { message = RunicText.Get("text_66b7c7710d67"); return false; } if (Object.op_Implicit((Object)(object)_player) && !((Object)(object)_player != (Object)(object)Player.m_localPlayer)) { Dictionary<string, string> customData = _player.m_customData; if (customData != null && customData.ContainsKey("runic.inventory.extra-row.v1")) { message = RunicText.Get("text_76aa803deabb"); if (_playerLoadInProgress || _rowMigration || !IsAuthoritativeLocal(_player) || _inventory == null) { return false; } if (!int.TryParse(_player.m_customData["runic.inventory.extra-row.v1"], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 4 || result > 9) { return false; } List<DedicatedRowItem> list = new List<DedicatedRowItem>(); foreach (ItemData allItem in _inventory.GetAllItems()) { if (allItem == null) { return false; } list.Add(new DedicatedRowItem(new InventorySlotCoordinate(allItem.m_gridPos.x, allItem.m_gridPos.y), ValheimContracts.Category(allItem), allItem.m_equipped)); } bool active = BetterArcheryCompatibility.Active; if (DedicatedRowPlan.TryCreate(result, enabled: false, _layout?.SpecialRow ?? result, list, null, out var _, out var _, out var reason, active, active ? (result + 1) : (-1))) { message = string.Empty; return true; } message = ((reason == "extra-row.clear-space-before-shrinking") ? RunicText.Get("text_6c143e4755b6") : RunicText.Get("text_9a609316cd7a")); return false; } } return true; } private void RestoreEnabled(string message) { _restoringEnabled = true; try { InventoryConfig.Enabled.Value = true; } finally { _restoringEnabled = false; } _disableCleanupPending = false; Notify(message); } internal bool HandlesNativeInventorySize(Player player) { if (!_disposed && !_batch && Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer) { ConfigEntry<bool> enabled = InventoryConfig.Enabled; if (enabled == null || !enabled.Value) { return player.m_customData?.ContainsKey("runic.inventory.extra-row.v1") ?? false; } return true; } return false; } internal void SetNativeInventorySize(Player player, int rows) { Rebind(player, "native-pocket-size", rows); } internal bool AllowInvalidItemCleanup(Humanoid actor) { if (!_disposed && !_batch && Object.op_Implicit((Object)(object)actor) && !((Object)(object)actor != (Object)(object)Player.m_localPlayer) && !((Object)(object)actor != (Object)(object)_player)) { Dictionary<string, string> customData = _player.m_customData; if (customData != null && customData.ContainsKey("runic.inventory.extra-row.v1")) { Inventory inventory = actor.GetInventory(); if (inventory == null || inventory != _inventory) { return false; } List<CleanupCoordinate> list = new List<CleanupCoordinate>(); foreach (ItemData allItem in inventory.GetAllItems()) { list.Add((allItem == null) ? new CleanupCoordinate(-1, -1) : new CleanupCoordinate(allItem.m_gridPos.x, allItem.m_gridPos.y)); } int retainedHeight; switch (InventoryCleanupPolicy.Evaluate(inventory.GetWidth(), inventory.GetHeight(), list, out retainedHeight)) { case InventoryCleanupDecision.NativeAllowed: return true; case InventoryCleanupDecision.RetainAndExpand: { inventory.SetHeight(retainedHeight); InventoryGui instance = InventoryGui.instance; if (instance != null) { instance.SetInventorySize(retainedHeight); } break; } } FailClosed("topology.external-resize-cleanup-prevented"); Diagnostics.Warn("Prevented automatic item ejection after an inventory dimension change. Items retained; height=" + inventory.GetHeight() + "."); return false; } } return true; } private bool EnsureDedicatedRow(int? requestedNativeRows, out string reason) { //IL_03f9: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) reason = "extra-row.not-ready"; if (_rowMigration || _playerLoadInProgress || !IsAuthoritativeLocal(_player) || _inventory == null || _inventory.GetWidth() != 8 || _player.m_customData == null) { return false; } bool flag = InventoryConfig.Enabled?.Value ?? false; Dictionary<string, string> customData = _player.m_customData; string value; bool flag2 = customData.TryGetValue("runic.inventory.extra-row.v1", out value); if (!flag && !flag2) { return true; } int result = 4; string s = default(string); if (_player.TryGetUniqueKeyValue("invrows", ref s) && !int.TryParse(s, NumberStyles.None, CultureInfo.InvariantCulture, out result)) { reason = "extra-row.invalid-native-size"; return false; } int num = result; if (requestedNativeRows.HasValue) { result = requestedNativeRows.Value; } if (result < 4 || result > 9) { reason = "extra-row.unsupported-native-size"; return false; } int result2 = -1; if (flag2 && (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result2) || result2 < 4 || result2 > 9)) { reason = "extra-row.invalid-marker"; return false; } bool active = BetterArcheryCompatibility.Active; string value2; bool flag3 = customData.TryGetValue("runic.inventory.betterarchery.v1", out value2); int quiverRow = BetterArcheryCompatibility.QuiverRow; int previousQuiverRow = -1; if (flag3) { if (!int.TryParse(value2, NumberStyles.None, CultureInfo.InvariantCulture, out var result3) || result3 < 4 || result3 > 9 || (flag2 && result2 != result3)) { reason = "extra-row.invalid-quiver-marker"; return false; } previousQuiverRow = result3 + 1; if (flag2) { result2 += 2; } } else if (active) { previousQuiverRow = num + 1; } string value3; bool flag4 = customData.TryGetValue("runic.inventory.topology.v1", out value3); PersistedTopologyState state = null; if (flag4 && !TopologyPersistenceCodec.TryDecode(value3, out state, out reason)) { return false; } if (flag2 && state != null && state.Height != result2 + 1) { reason = "extra-row.metadata-mismatch"; return false; } if (!flag2 && state != null) { result2 = state.Height - 1; } int height = result + (active ? 2 : 0) + (flag ? 1 : 0); int height2 = _inventory.GetHeight(); if (height2 < 4 || height2 > 12) { reason = "extra-row.unsupported-live-size"; return false; } List<ItemData> allItems = _inventory.GetAllItems(); int num2 = height2; foreach (ItemData item in allItems) { if (item == null || item.m_gridPos.y < 0 || item.m_gridPos.y >= 12) { reason = "extra-row.invalid-item-position"; return false; } num2 = Math.Max(num2, item.m_gridPos.y + 1); } List<DedicatedRowItem> list = new List<DedicatedRowItem>(); foreach (ItemData item2 in allItems) { list.Add(new DedicatedRowItem(new InventorySlotCoordinate(item2.m_gridPos.x, item2.m_gridPos.y), ValheimContracts.Category(item2), item2.m_equipped)); } if (!DedicatedRowPlan.TryCreate(result, flag, result2, list, state?.LockedSlots(), out var positions, out var mappedLocks, out reason, active, previousQuiverRow)) { _inventory.SetHeight(num2); InventoryGui instance = InventoryGui.instance; if (instance != null) { instance.SetInventorySize(num2); } return false; } string payload = null; if (flag && (!TopologyLayout.TryCreate(8, height, out var layout, out reason) || !TopologyPersistenceCodec.TryEncode(layout, mappedLocks, out payload, out reason))) { return false; } if (!TryEnterMutation("runic.inventory/dedicated-row", out var lease)) { reason = "extra-row.transaction-busy"; return false; } IReadOnlyList<ItemMutationEvidence> evidence = null; _rowMigration = true; Inventory inventory = _inventory; inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(OnInventoryChanged)); try { using (lease) { _inventory.SetHeight(num2); if (!InventoryEvidence.TryCaptureMutation(_inventory, out evidence, out reason)) { throw new InvalidOperationException(reason); } _inventory.SetHeight(height); for (int i = 0; i < allItems.Count; i++) { allItems[i].m_gridPos = new Vector2i(positions[i].X, positions[i].Y); } if (!InventoryEvidence.VerifyUnchangedExceptPosition(_inventory, evidence, out reason)) { throw new InvalidOperationException(reason); } if (flag) { customData["runic.inventory.extra-row.v1"] = result.ToString(CultureInfo.InvariantCulture); customData["runic.inventory.topology.v1"] = payload; } else { customData.Remove("runic.inventory.extra-row.v1"); customData.Remove("runic.inventory.topology.v1"); } if (active) { customData["runic.inventory.betterarchery.v1"] = result.ToString(CultureInfo.InvariantCulture); BetterArcheryCompatibility.SetQuiverRow(result + 1); } else { customData.Remove("runic.inventory.betterarchery.v1"); } if (requestedNativeRows.HasValue) { _player.AddUniqueKeyValue("invrows", result.ToString(CultureInfo.InvariantCulture)); } } } catch (Exception exception) { InventoryEvidence.RestorePositions(evidence); _inventory.SetHeight(num2); if (flag2) { customData["runic.inventory.extra-row.v1"] = value; } else { customData.Remove("runic.inventory.extra-row.v1"); } if (flag4) { customData["runic.inventory.topology.v1"] = value3; } else { customData.Remove("runic.inventory.topology.v1"); } if (flag3) { customData["runic.inventory.betterarchery.v1"] = value2; } else { customData.Remove("runic.inventory.betterarchery.v1"); } if (active) { BetterArcheryCompatibility.SetQuiverRow(quiverRow); } reason = "extra-row.migration-failed"; Diagnostics.Error(exception, RunicText.Get("text_7506f8257cce")); return false; } finally { Inventory inventory2 = _inventory; inventory2.m_onChanged = (Action)Delegate.Combine(inventory2.m_onChanged, new Action(OnInventoryChanged)); _rowMigration = false; InventoryGui instance2 = InventoryGui.instance; if (instance2 != null) { instance2.SetInventorySize(_inventory.GetHeight()); } } reason = "ok"; return true; } private bool HasDedicatedRow(Inventory inventory) { if (!_disposed && !_playerLoadInProgress && !_rowMigration) { ConfigEntry<bool> enabled = InventoryConfig.Enabled; if (enabled != null && enabled.Value && (Object)(object)_player == (Object)(object)Player.m_localPlayer && inventory == _inventory) { Player player = _player; if (player == null) { return false; } return player.m_customData?.ContainsKey("runic.inventory.extra-row.v1") == true; } } return false; } internal bool AllowPositionedAddition(Inventory inventory, ItemData item, int x, int y, bool loading) { //IL_005f: 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_0073: Invalid comparison between Unknown and I4 if (loading || !HasDedicatedRow(inventory)) { return true; } if (x < 0 || x >= inventory.GetWidth() || y < 0 || y >= inventory.GetHeight()) { return false; } if (IsQuiverReservedRow(y)) { if (y == inventory.GetHeight() - 2 && x < 3 && item != null && (int)(item.m_shared?.m_itemType).GetValueOrDefault() == 9) { return BetterArcheryCompatibility.Equipped; } return false; } if (y == inventory.GetHeight() - 1) { if (item != null) { return TopologyLayout.Accepts((InventoryRoleKind)(x + 1), ValheimContracts.Category(item)); } return false; } return true; } private bool IsQuiverReservedRow(int y) { if (HasQuiverLayout && _inventory != null) { if (y != _inventory.GetHeight() - 3) { return y == _inventory.GetHeight() - 2; } return true; } return false; } internal InventoryRuntime(bool batch) { _batch = batch; _mainThreadId = Thread.CurrentThread.ManagedThreadId; _filters = PickupFilterSet.Parse(InventoryConfig.FilteredPickupItems?.Value ?? string.Empty); _mode = (batch ? InventoryAuthorityMode.BatchInert : InventoryAuthorityMode.Unavailable); _reasonCode = (batch ? "authority.batch-inert" : "player.not-loaded"); } internal void Initialize() { if (_batch) { RebuildStatusOnly(); } else { Rebind(Player.m_localPlayer, "startup"); } } internal void Tick() { //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_0176: 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_01df: Unknown result type (might be due to invalid IL or missing references) if (_disposed || _batch) { return; } if (_player != Player.m_localPlayer) { Rebind(Player.m_localPlayer, "local-player-changed"); } if (!Object.op_Implicit((Object)(object)_player) || _inventory == null) { return; } if (_disableCleanupPending) { ConfigEntry<bool> enabled = InventoryConfig.Enabled; if (enabled != null && enabled.Value) { _disableCleanupPending = false; Rebind(Player.m_localPlayer, "disable-cleanup-cancelled"); } else { if (!TryCompleteDisableCleanup()) { return; } Rebind(Player.m_localPlayer, "disable-cleanup-completed"); } if (!Object.op_Implicit((Object)(object)_player) || _inventory == null) { return; } } if (_layout != null && !LiveDimensionsMatch()) { FailClosed("topology.runtime-dimension-changed"); return; } bool flag = IsAuthoritativeLocal(_player); if ((_mode == InventoryAuthorityMode.AuthoritativeLocal && !flag) || (_mode == InventoryAuthorityMode.RemoteDedicatedCompatibility && flag)) { Rebind(_player, "authority-changed"); if (!Object.op_Implicit((Object)(object)_player) || _inventory == null) { return; } } if (!AcceptsInput) { return; } bool flag2 = (Object)(object)InventoryGui.instance != (Object)null && InventoryGui.IsVisible(); if (flag2) { if (KeyboardInput.ShortcutDown(InventoryConfig.Sort.Value)) { SortSelectedRows("keyboard"); } if (KeyboardInput.ShortcutDown(InventoryConfig.ToggleLock.Value)) { ToggleFocusedLock("keyboard"); } } else if (ValheimContracts.PlayerMayTakeInput(_player)) { if (KeyboardInput.ShortcutDown(InventoryConfig.Quick1.Value)) { UseQuick(InventoryRoleKind.Quick1, "keyboard"); } else if (KeyboardInput.ShortcutDown(InventoryConfig.Quick2.Value)) { UseQuick(InventoryRoleKind.Quick2, "keyboard"); } else if (KeyboardInput.ShortcutDown(InventoryConfig.Quick3.Value)) { UseQuick(InventoryRoleKind.Quick3, "keyboard"); } } ControllerInput.Tick(this, flag2, !flag2 && ValheimContracts.PlayerMayTakeInput(_player)); } internal void Draw() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) if (_disposed || _batch || (Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.IsVisible() || ValheimContracts.InventoryModalVisible()) { return; } if (TopologyActive) { ConfigEntry<bool> showRoleLabels = InventoryConfig.ShowRoleLabels; if (showRoleLabels == null || showRoleLabels.Value) { DrawRoleOverlay(); } } if (CanEnforceLocks()) { DrawLockedSlotOverlay(); } ConfigEntry<bool> showInventoryStatus = InventoryConfig.ShowInventoryStatus; if (showInventoryStatus != null && showInventoryStatus.Value) { float num = Math.Min(500f, Math.Max(300f, (float)Screen.width - 20f)); GUI.Box(new Rect(Math.Max(10f, (float)Screen.width - num - 10f), 72f, num, 190f), _statusText); } } private void DrawRoleOverlay() { //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) InventoryGrid val = InventoryGui.instance?.m_playerGrid; if (!Object.op_Implicit((Object)(object)val) || _layout == null || !ValheimContracts.TryBottomRowScreenRects(val, _layout.SpecialRow, _roleSlotRects)) { return; } Color color = GUI.color; int depth = GUI.depth; try { GUI.depth = -650; Rect val2 = _roleSlotRects[0]; Rect val3 = _roleSlotRects[_roleSlotRects.Length - 1]; Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(((Rect)(ref val2)).xMin - 2f, Math.Min(((Rect)(ref val2)).yMin, ((Rect)(ref val3)).yMin) - 2f, ((Rect)(ref val3)).xMax - ((Rect)(ref val2)).xMin + 4f, Math.Max(((Rect)(ref val2)).yMax, ((Rect)(ref val3)).yMax) - Math.Min(((Rect)(ref val2)).yMin, ((Rect)(ref val3)).yMin) + 4f); GUI.color = new Color(0.7f, 0.43f, 0.13f, 0.48f); GUI.DrawTexture(new Rect(((Rect)(ref val4)).xMin, ((Rect)(ref val4)).yMin, ((Rect)(ref val4)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref val4)).xMin, ((Rect)(ref val4)).yMax - 1f, ((Rect)(ref val4)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref val4)).xMin, ((Rect)(ref val4)).yMin, 1f, ((Rect)(ref val4)).height), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref val4)).xMax - 1f, ((Rect)(ref val4)).yMin, 1f, ((Rect)(ref val4)).height), (Texture)(object)Texture2D.whiteTexture); GUIStyle val5 = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)7, fontStyle = (FontStyle)1, fontSize = Math.Max(8, Math.Min(11, (int)(((Rect)(ref _roleSlotRects[0])).width / 7f))), clipping = (TextClipping)1, wordWrap = false }; Rect val7 = default(Rect); for (int i = 0; i < _roleSlotRects.Length; i++) { Rect val6 = _roleSlotRects[i]; GUI.color = new Color(0f, 0f, 0f, 0.7f); GUI.DrawTexture(new Rect(((Rect)(ref val6)).x + 1f, ((Rect)(ref val6)).y + 1f, ((Rect)(ref val6)).width - 2f, 17f), (Texture)(object)Texture2D.whiteTexture); ((Rect)(ref val7))..ctor(((Rect)(ref val6)).x + 2f, ((Rect)(ref val6)).y + 1f, ((Rect)(ref val6)).width - 4f, 16f); val5.normal.textColor = new Color(0.05f, 0.03f, 0.01f, 0.92f); GUI.Label(new Rect(((Rect)(ref val7)).x + 1f, ((Rect)(ref val7)).y + 1f, ((Rect)(ref val7)).width, ((Rect)(ref val7)).height), RoleLabels[i], val5); val5.normal.textColor = new Color(1f, 0.78f, 0.34f, 0.98f); GUI.Label(val7, RoleLabels[i], val5); } } finally { GUI.color = color; GUI.depth = depth; } } private void DrawLockedSlotOverlay() { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_00c6: Unknown result type (might be due to invalid IL or missing references) InventoryGrid val = InventoryGui.instance?.m_playerGrid; if (!Object.op_Implicit((Object)(object)val) || _layout == null || _persisted == null) { return; } Color color = GUI.color; int depth = GUI.depth; try { GUI.depth = -660; GUI.color = new Color(1f, 0.84f, 0.08f, 1f); foreach (InventorySlotCoordinate item in _persisted.LockedSlots()) { if (ValheimContracts.TrySlotScreenRect(val, item.X, item.Y, out var slot)) { DrawOutline(new Rect(((Rect)(ref slot)).xMin - 2f, ((Rect)(ref slot)).yMin - 2f, ((Rect)(ref slot)).width + 4f, ((Rect)(ref slot)).height + 4f), 4f); } } } finally { GUI.color = color; GUI.depth = depth; } } private static void DrawOutline(Rect rect, float thickness) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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) GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMin, ((Rect)(ref rect)).width, thickness), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMax - thickness, ((Rect)(ref rect)).width, thickness), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMin, thickness, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - thickness, ((Rect)(ref rect)).yMin, thickness, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); } internal void OnConfigurationChanged() { if (_disposed || _restoringEnabled) { return; } ConfigEntry<bool> enabled = InventoryConfig.Enabled; if ((enabled == null || !enabled.Value) && !CanDisable(out var message)) { RestoreEnabled(message); return; } if ((_filters = PickupFilterSet.Parse(InventoryConfig.FilteredPickupItems.Value)).Truncated) { Diagnostics.Warn("Pickup filter exceeded 128 safe unique rules; additional entries were ignored."); } ControllerBindings.Invalidate(); ControllerChordSession.Reset(); ConfigEntry<bool> enabled2 = InventoryConfig.Enabled; if (enabled2 != null && enabled2.Value) { _disableCleanupPending = false; } Rebind(Player.m_localPlayer, "configuration-changed"); } internal void OnLocalPlayerChanged(Player player) { Rebind(player, "set-local-player"); } internal void OnPlayerLoadStarted(Player player) { if (!_disposed && !((Object)(object)player != (Object)(object)_player) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _playerLoadInProgress = true; _loadMetadataRefreshed = false; player.m_customData?.Remove("runic.inventory.extra-row.v1"); player.m_customData?.Remove("runic.inventory.betterarchery.v1"); player.m_customData?.Remove("runic.inventory.topology.v1"); } } internal void OnPlayerLoadCompleted(Player player) { if (!_disposed && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _playerLoadInProgress = false; _loadMetadataRefreshed = false; Rebind(player, "player-load-completed"); } } internal void OnPlayerLoadFaulted(Player player) { if (!_disposed && !((Object)(object)player != (Object)(object)_player)) { _playerLoadInProgress = false; _loadMetadataRefreshed = false; FailClosed("player.load-faulted"); } } internal void OnNativeInventorySizeChanged(Player player, int previousHeight) { //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid