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 InnangardBetrValheimDrawerBridge v1.0.5
plugins/InnangardBetrValheimDrawerBridge.dll
Decompiled 2 months 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.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using API; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using InnangardBetrValheimDrawerBridge; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; using kg_ItemDrawers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("kg_ItemDrawers")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("kg_ItemDrawers")] [assembly: AssemblyTitle("kg_ItemDrawers")] [assembly: AssemblyVersion("1.0.0.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 API { public static class ClientSideV2 { public static List<ZNetView> AllDrawers() { return Bridge.AllViews(); } } public static class ClientSide { public static List<ZNetView> AllDrawers() { return Bridge.AllViews(); } } public static class ItemDrawers_API { public sealed class Drawer { private readonly ZNetView _znv; public readonly string Prefab; public readonly Vector3 Position; public readonly int Amount; public readonly int Quality; public Drawer(ZNetView znv) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //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) _znv = znv; if ((Object)(object)znv == (Object)null || !znv.IsValid() || znv.GetZDO() == null) { Prefab = string.Empty; Position = Vector3.zero; Amount = 0; Quality = 1; } else { ZDO zDO = znv.GetZDO(); Prefab = zDO.GetString("Prefab", string.Empty); Position = ((Component)znv).transform.position; Amount = zDO.GetInt("Amount", 0); Quality = Math.Max(1, zDO.GetInt("Quality", 1)); } } public void Add(int amount, int quality) { Bridge.ApiAdd(_znv, Prefab, amount, quality); } public void Remove(int amount) { Bridge.ApiRemove(_znv, amount); } } public static List<Drawer> AllDrawers => (from v in Bridge.AllViews() select new Drawer(v)).ToList(); public static List<Drawer> AllDrawersInRange(Vector3 position, float range) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) Bridge.RememberRange(position, range); float num = range * range; List<ZNetView> list = Bridge.AllViews(); List<Drawer> list2 = new List<Drawer>(list.Count); foreach (ZNetView item in list) { if (!((Object)(object)item == (Object)null) && item.IsValid()) { Vector3 val = ((Component)item).transform.position - position; if (!(((Vector3)(ref val)).sqrMagnitude > num)) { list2.Add(new Drawer(item)); } } } if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[RANGE] pos={position} range={range:0.0} returned={list2.Count}/{list.Count}"); } return list2; } } } namespace kg_ItemDrawers { public sealed class DrawerComponent : MonoBehaviour { public static readonly List<DrawerComponent> AllDrawers = new List<DrawerComponent>(); internal object RealDrawer; internal ZNetView View; public string CurrentPrefab => Bridge.ResolvePrefabName(Bridge.GetAssigned(RealDrawer)); public int CurrentAmount => Bridge.GetCount(RealDrawer); internal void Bind(object real, ZNetView view) { RealDrawer = real; View = view; } internal void Sync() { if (RealDrawer == null || (Object)(object)View == (Object)null || !View.IsValid()) { return; } ZDO zDO = View.GetZDO(); if (zDO != null && View.IsOwner()) { string text = CurrentPrefab ?? string.Empty; int num = ((!string.IsNullOrEmpty(text)) ? Math.Max(0, CurrentAmount) : 0); if (zDO.GetString("Prefab", string.Empty) != text) { zDO.Set("Prefab", text); } if (zDO.GetInt("Amount", 0) != num) { zDO.Set("Amount", num); } if (zDO.GetInt("Quality", 1) != 1) { zDO.Set("Quality", 1); } } } private void OnDestroy() { AllDrawers.Remove(this); RealDrawer = null; View = null; } } } namespace InnangardBetrValheimDrawerBridge { [BepInPlugin("com.phoenixf.innangard.betrvalheimdrawerbridge", "Innangard BetrValheim Drawer Bridge", "0.5.18-betr458-loading-config-ui-typefix")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string ModGuid = "com.phoenixf.innangard.betrvalheimdrawerbridge"; public const string ModName = "Innangard BetrValheim Drawer Bridge"; public const string ModVersion = "0.5.18-betr458-loading-config-ui-typefix"; internal static ManualLogSource Log; internal static ConfigEntry<bool> DetailedLogging; internal static ConfigEntry<bool> DeliverBeforeCollectingMore; internal static ConfigEntry<bool> SortAfterStall; internal static ConfigEntry<bool> ReduceClaimRenewals; internal static ConfigEntry<bool> DrawerUseChestApproach; private float _nextRefresh; private Harmony _harmony; private bool _caretakerPatchInstalled; private bool _sortRangePatchInstalled; private bool _boRulesPatchInstalled; private bool _claimRenewalPatchInstalled; private bool _boUiPatchInstalled; private bool _betterOrganisationInstalled; private bool _drawerMovementPatchInstalled; private bool _loadingScreenConfigPatchInstalled; private void Awake() { //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; DetailedLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Detailed logging", false, "Log individual bridged drawers and API range queries. Add/remove failures are always logged."); DeliverBeforeCollectingMore = ((BaseUnityPlugin)this).Config.Bind<bool>("Caretaker", "Deliver before collecting more", true, "When a caretaker has already collected sorting cargo, deliver it physically before visiting another chest. Prevents CollectMore from trapping an undelivered load."); SortAfterStall = ((BaseUnityPlugin)this).Config.Bind<bool>("Caretaker", "Deposit when stuck near storage", true, "After a caretaker gets stuck near its sorting destination, allow a deposit within Innangard's usual interaction distance."); ReduceClaimRenewals = ((BaseUnityPlugin)this).Config.Bind<bool>("Networking", "Reduce station claim renewals", true, "Renew Innangard work claims only after one third of their lease has elapsed, reducing redundant ZDO updates."); DrawerUseChestApproach = ((BaseUnityPlugin)this).Config.Bind<bool>("Drawer Movement", "Use chest reach and standing positions", true, "Make Innangard drawers use the same StorageReach and chest-style standing-position search as normal storage instead of the hardcoded drawer reach/direct approach."); try { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Innangard BetrValheim Drawer Bridge 0.5.18-betr458-loading-config-ui-typefix starting."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Assembly identity is kg_ItemDrawers. Drawer storage uses the ItemDrawers API; caretaker sorting delivers first and deposits nearby only after getting stuck."); if (!Bridge.BindBetrValheim()) { ((BaseUnityPlugin)this).Logger.LogError((object)"BetrValheim drawer binding failed; bridge is inactive."); return; } Bridge.LogApiSurface(); _betterOrganisationInstalled = AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase)); if (_betterOrganisationInstalled) { BetterOrganisationRules.Bind(); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BO] Better Organisation not installed; all BO-specific integration is disabled. Drawer/caretaker/network patches remain active."); } try { _harmony = new Harmony("com.phoenixf.innangard.betrvalheimdrawerbridge"); _caretakerPatchInstalled = CaretakerDeliverFirst.Install(_harmony); _sortRangePatchInstalled = CaretakerSortRange.Install(_harmony); if (_betterOrganisationInstalled) { _boRulesPatchInstalled = BetterOrganisationRules.Install(_harmony); } _claimRenewalPatchInstalled = StationClaimRenewal.Install(_harmony); _drawerMovementPatchInstalled = DrawerMovementCompat.Install(_harmony); _loadingScreenConfigPatchInstalled = BetrValheimLoadingScreenConfigRedirect.Install(_harmony); if (_betterOrganisationInstalled) { _boUiPatchInstalled = BetterOrganisationChestPanelCompat.Install(_harmony); } } catch (Exception e) { ((BaseUnityPlugin)this).Logger.LogError((object)("[Patch] Innangard patch failed: " + Bridge.Root(e))); } Bridge.Refresh(forceSummary: true); if (_betterOrganisationInstalled) { BetterOrganisationRules.Refresh(forceSummary: true); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Innangard BetrValheim Drawer Bridge 0.5.18-betr458-loading-config-ui-typefix loaded."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Expected Innangard log: compatible ItemDrawers API detected; sorting and supply access enabled."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Bridge initialisation failed: " + ex)); } } private void Update() { if (Time.unscaledTime < _nextRefresh) { return; } _nextRefresh = Time.unscaledTime + 0.5f; try { if (!_caretakerPatchInstalled && _harmony != null) { _caretakerPatchInstalled = CaretakerDeliverFirst.Install(_harmony); } if (!_sortRangePatchInstalled && _harmony != null) { _sortRangePatchInstalled = CaretakerSortRange.Install(_harmony); } if (_betterOrganisationInstalled && !_boRulesPatchInstalled && _harmony != null) { _boRulesPatchInstalled = BetterOrganisationRules.Install(_harmony); } if (!_claimRenewalPatchInstalled && _harmony != null) { _claimRenewalPatchInstalled = StationClaimRenewal.Install(_harmony); } if (!_drawerMovementPatchInstalled && _harmony != null) { _drawerMovementPatchInstalled = DrawerMovementCompat.Install(_harmony); } if (!_loadingScreenConfigPatchInstalled && _harmony != null) { _loadingScreenConfigPatchInstalled = BetrValheimLoadingScreenConfigRedirect.Install(_harmony); } if (_betterOrganisationInstalled && !_boUiPatchInstalled && _harmony != null) { _boUiPatchInstalled = BetterOrganisationChestPanelCompat.Install(_harmony); } Bridge.Refresh(forceSummary: false); if (_betterOrganisationInstalled) { BetterOrganisationRules.Refresh(forceSummary: false); } } catch (Exception e) { ((BaseUnityPlugin)this).Logger.LogError((object)("[Refresh] " + Bridge.Root(e))); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if (_betterOrganisationInstalled) { BetterOrganisationRules.RestoreAll(); } Bridge.Clear(); } } internal static class BetrValheimLoadingScreenConfigRedirect { private static bool _installed; private static MethodInfo _load; private static FieldInfo _backgroundSprite; private static FieldInfo _logoSprite; private static FieldInfo _missing; private static string _originalFolder; private static bool _customBackgroundApplied; private static bool _customLogoApplied; private static bool _failureLogged; internal static bool Install(Harmony harmony) { //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Expected O, but got Unknown if (_installed) { return true; } if (harmony == null) { return false; } try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "BetrValheim", StringComparison.OrdinalIgnoreCase)); Type type = assembly?.GetType("BetrValheim.UI.LoadingScreen", throwOnError: false); if (type == null && assembly != null) { try { type = assembly.GetTypes().FirstOrDefault((Type t) => string.Equals(t.Name, "LoadingScreen", StringComparison.Ordinal) && AccessTools.Method(t, "Build", new Type[1] { typeof(Hud) }, (Type[])null) != null && AccessTools.Method(t, "Load", new Type[2] { typeof(string), typeof(string) }, (Type[])null) != null && AccessTools.Field(t, "_logoSprite") != null && AccessTools.Field(t, "_backgroundSprite") != null); } catch { type = null; } } if (assembly == null || type == null) { LogFailureOnce("BetrValheim 4.58 LoadingScreen type was not found."); return false; } MethodInfo methodInfo = AccessTools.Method(type, "Build", new Type[1] { typeof(Hud) }, (Type[])null); _load = AccessTools.Method(type, "Load", new Type[2] { typeof(string), typeof(string) }, (Type[])null); _backgroundSprite = AccessTools.Field(type, "_backgroundSprite"); _logoSprite = AccessTools.Field(type, "_logoSprite"); _missing = AccessTools.Field(type, "_missing"); _originalFolder = Path.GetDirectoryName(assembly.Location); if (methodInfo == null || _load == null || _backgroundSprite == null || _logoSprite == null) { LogFailureOnce("BetrValheim 4.58 loading-screen members were not found."); return false; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BetrValheimLoadingScreenConfigRedirect), "BuildPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _installed = true; return true; } catch (Exception e) { LogFailureOnce("BetrValheim 4.58 loading-image patch failed: " + Bridge.Root(e)); return false; } } private static void BuildPrefix() { try { PrepareSprite("loading_background.jpg", _backgroundSprite, ref _customBackgroundApplied); PrepareSprite("loading_logo.png", _logoSprite, ref _customLogoApplied); if (_backgroundSprite.GetValue(null) is Sprite && _logoSprite.GetValue(null) is Sprite) { _missing?.SetValue(null, false); } } catch { } } private static void PrepareSprite(string name, FieldInfo field, ref bool customApplied) { if (field == null || _load == null) { return; } string text = Path.Combine(Paths.ConfigPath, "BetrValheim"); if (File.Exists(Path.Combine(text, name))) { if (!customApplied) { object? obj = _load.Invoke(null, new object[2] { text, name }); Sprite val = (Sprite)((obj is Sprite) ? obj : null); if ((Object)(object)val != (Object)null) { field.SetValue(null, val); customApplied = true; } } } else if (field.GetValue(null) == null && !string.IsNullOrEmpty(_originalFolder)) { object? obj2 = _load.Invoke(null, new object[2] { _originalFolder, name }); Sprite val2 = (Sprite)((obj2 is Sprite) ? obj2 : null); if ((Object)(object)val2 != (Object)null) { field.SetValue(null, val2); } } } private static void LogFailureOnce(string message) { if (!_failureLogged) { _failureLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Loading Screen] " + message)); } } } } internal static class StationClaimRenewal { private static bool _installed; private static bool _reportedMissing; internal static bool Install(Harmony harmony) { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown if (_installed) { return true; } Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.StationClaim", throwOnError: false); if (type == null) { return false; } MethodInfo method = type.GetMethod("Renew", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(ZNetView), typeof(ZDOID), typeof(float) }, null); if (method == null || method.ReturnType != typeof(void)) { if (!_reportedMissing) { _reportedMissing = true; Plugin.Log.LogWarning((object)"[Network] StationClaim.Renew signature changed; claim throttling unavailable."); } return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(StationClaimRenewal), "RenewPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _installed = true; Plugin.Log.LogInfo((object)"[Network] Innangard station claim renewals reduced while the lease is healthy."); return true; } private static bool RenewPrefix(ZNetView __0, ZDOID __1, float __2) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (Plugin.ReduceClaimRenewals == null || !Plugin.ReduceClaimRenewals.Value || (Object)(object)__0 == (Object)null || !__0.IsValid() || !__0.IsOwner() || __1 == ZDOID.None || __2 <= 0f) { return true; } ZDO zDO = __0.GetZDO(); if (zDO == null || zDO.GetZDOID("inn_station_claim") != __1) { return true; } long num = zDO.GetLong("inn_station_claim_until", 0L); long ticks = DateTime.UtcNow.Ticks; long num2 = (long)((double)(__2 * 10000000f) * (2.0 / 3.0)); if (num > ticks) { return num - ticks <= num2; } return true; } } internal static class CaretakerDeliverFirst { private static FieldInfo _load; private static FieldInfo _remaining; private static bool _reportedDeferred; private static bool _reportedMissingMember; internal static bool Install(Harmony harmony) { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.Jobs.CaretakerJob", throwOnError: false); if (type == null) { if (!_reportedDeferred) { _reportedDeferred = true; Plugin.Log.LogInfo((object)"[Caretaker] Innangard is not loaded yet; will bind the deliver-first patch when it loads."); } return false; } MethodInfo method = type.GetMethod("TryCollectFromAnother", BindingFlags.Instance | BindingFlags.NonPublic); _load = type.GetField("_load", BindingFlags.Instance | BindingFlags.NonPublic); _remaining = type.GetField("_remaining", BindingFlags.Instance | BindingFlags.NonPublic); if (method == null || method.ReturnType != typeof(void) || _load == null || _remaining == null || _remaining.FieldType != typeof(int)) { if (!_reportedMissingMember) { _reportedMissingMember = true; Plugin.Log.LogError((object)"[Caretaker] Innangard caretaker method or queued-delivery field is missing; deliver-first patch could not bind."); } return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(CaretakerDeliverFirst), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[Caretaker] Deliver-first patch active: carry one chest's sorting cargo to its destination before collecting from another chest."); return true; } private static bool Prefix(object __instance) { if (!Plugin.DeliverBeforeCollectingMore.Value || __instance == null) { return true; } try { ICollection collection = _load.GetValue(__instance) as ICollection; int num = (int)_remaining.GetValue(__instance); if (num <= 0 && (collection == null || collection.Count == 0)) { return true; } if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[Caretaker] Skipped CollectMore with remaining={num}, queued={collection?.Count ?? 0}; proceeding to physical delivery."); } return false; } catch (Exception e) { Plugin.Log.LogWarning((object)("[Caretaker] Could not read pending deliveries: " + Bridge.Root(e))); return true; } } } internal static class CaretakerSortRange { private sealed class StallState { internal GameObject Goal; internal Vector3 Position; internal float Since; } private sealed class Context { internal Component Settler; } private static FieldInfo _chore; private static FieldInfo _remaining; private static FieldInfo _target; private static MethodInfo _putAway; private static PropertyInfo _ai; private static PropertyInfo _pathBlocked; private static readonly ConditionalWeakTable<object, StallState> Stalls = new ConditionalWeakTable<object, StallState>(); [ThreadStatic] private static Context _active; internal static bool Install(Harmony harmony) { //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01b0: Expected O, but got Unknown //IL_01b0: Expected O, but got Unknown Assembly? assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase)); Type type = assembly?.GetType("Innangard.Jobs.CaretakerJob", throwOnError: false); Type type2 = assembly?.GetType("Innangard.SettlerController", throwOnError: false); Type type3 = assembly?.GetType("Innangard.SettlerAI", throwOnError: false); if (type == null || type2 == null || type3 == null) { return false; } _chore = type.GetField("_chore", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _remaining = type.GetField("_remaining", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _target = type.GetField("_target", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _putAway = type.GetMethod("PutAway", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _ai = type2.GetProperty("Ai", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _pathBlocked = type3.GetProperty("PathBlocked", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method = type.GetMethod("Pursue", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (_chore == null || _remaining == null || _target == null || _putAway == null || _ai == null || _pathBlocked == null || method == null) { Plugin.Log.LogWarning((object)"[Caretaker] Stalled-deposit patch could not bind to this Innangard version."); return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(CaretakerSortRange), "Begin", (Type[])null), new HarmonyMethod(typeof(CaretakerSortRange), "End", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CaretakerSortRange), "Finish", (Type[])null), (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[Caretaker] Sorting deposits after a blocked approach or 3 seconds stalled nearby."); return true; } private static void Begin(object __instance, object __0, out Context __state) { __state = _active; _active = null; if (!Plugin.SortAfterStall.Value || __instance == null) { return; } try { int num = Convert.ToInt32(_chore.GetValue(__instance)); if ((num == 1 || num == 2) && (int)_remaining.GetValue(__instance) > 0) { _active = new Context { Settler = (Component)((__0 is Component) ? __0 : null) }; } } catch (Exception e) { Plugin.Log.LogWarning((object)("[Caretaker] Stalled-deposit context failed: " + Bridge.Root(e))); } } private static void End(object __instance, ref bool __result, Context __state) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_active?.Settler == (Object)null || (int)_remaining.GetValue(__instance) <= 0) { Stalls.Remove(__instance); return; } object? value = _target.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if (!((Object)(object)val == (Object)null) && val.activeInHierarchy) { Vector3 position = _active.Settler.transform.position; StallState value2 = Stalls.GetValue(__instance, (object _) => new StallState()); if ((Object)(object)value2.Goal != (Object)(object)val || Vector3.Distance(value2.Position, position) > 0.4f) { value2.Goal = val; value2.Position = position; value2.Since = Time.time; } float range = ((Convert.ToInt32(_chore.GetValue(__instance)) == 2) ? DrawerMovementCompat.EffectiveDrawerReach(3.55f) : 2.8f); bool flag = Time.time - value2.Since >= 3f; bool flag2 = Blocked(); if ((__result || flag2) && (flag || flag2) && InRange(_active.Settler, val, range)) { _putAway.Invoke(__instance, new object[1] { _active.Settler }); __result = true; Stalls.Remove(__instance); Plugin.Log.LogInfo((object)("[Caretaker] Deposited sorting cargo after " + (flag ? "standing still" : "blocked route") + " near " + ((Object)val).name)); } } } catch (Exception e) { Plugin.Log.LogWarning((object)("[Caretaker] Stalled deposit failed: " + Bridge.Root(e))); } finally { _active = __state; } } private static Exception Finish(Exception __exception, Context __state) { _active = __state; return __exception; } private static bool Blocked() { object value = _ai.GetValue(_active.Settler, null); if (value != null) { return (bool)_pathBlocked.GetValue(value, null); } return false; } private static bool InRange(Component settler, GameObject target, float range) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_007a: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) Vector3 val = settler.transform.position + Vector3.up * 1.05f; float num = float.MaxValue; Collider[] componentsInChildren = target.GetComponentsInChildren<Collider>(true); foreach (Collider val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && val2.enabled && !val2.isTrigger) { num = Mathf.Min(num, Vector3.Distance(val, val2.ClosestPoint(val))); } } if (num == float.MaxValue) { num = Vector3.Distance(val, target.transform.position); } return num <= range; } } internal static class DrawerMovementCompat { private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static bool _installed; private static bool _reportedMissing; private static MethodInfo _approachStorage; private static PropertyInfo _storageReach; [ThreadStatic] private static int _insideApproachStorage; internal static bool Install(Harmony harmony) { //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Expected O, but got Unknown //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Expected O, but got Unknown if (_installed) { return true; } Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase)); Type controller = assembly?.GetType("Innangard.SettlerController", throwOnError: false); Type type = assembly?.GetType("Innangard.ItemDrawerBridge", throwOnError: false); if (controller == null || type == null) { return false; } MethodInfo method = controller.GetMethod("Approach", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(GameObject), typeof(float), typeof(bool) }, null); _approachStorage = controller.GetMethod("ApproachStorage", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(GameObject), typeof(bool) }, null); _storageReach = controller.GetProperty("StorageReach", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m) { if (m.Name != "CanReach" || m.ReturnType != typeof(bool)) { return false; } ParameterInfo[] parameters = m.GetParameters(); return parameters.Length == 3 && parameters[0].ParameterType == controller && parameters[2].ParameterType == typeof(float); }); if (method == null || _approachStorage == null || _storageReach == null || methodInfo == null) { if (!_reportedMissing) { _reportedMissing = true; Plugin.Log.LogWarning((object)("[Drawer Movement] Innangard storage movement binding incomplete; " + $"Approach={method != null} ApproachStorage={_approachStorage != null} " + $"StorageReach={_storageReach != null} CanReach={methodInfo != null}.")); } return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(DrawerMovementCompat), "ApproachPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(DrawerMovementCompat), "CanReachPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _installed = true; Plugin.Log.LogInfo((object)"[Drawer Movement] Drawers now use Innangard StorageReach and the same standing-position search as chests."); return true; } internal static float EffectiveDrawerReach(float originalReach) { if (Plugin.DrawerUseChestApproach == null || !Plugin.DrawerUseChestApproach.Value) { return originalReach; } float num = ReadStorageReach(); if (num <= 0f) { return originalReach; } return Mathf.Min(originalReach, num); } private static float ReadStorageReach() { try { if (_storageReach == null) { return -1f; } object value = _storageReach.GetValue(null, null); return (value == null) ? (-1f) : Convert.ToSingle(value); } catch { return -1f; } } private static void CanReachPrefix(ref float __2) { __2 = EffectiveDrawerReach(__2); } private static bool ApproachPrefix(object __instance, GameObject __0, bool __2) { if (Plugin.DrawerUseChestApproach == null || !Plugin.DrawerUseChestApproach.Value || __instance == null || (Object)(object)__0 == (Object)null || _approachStorage == null || _insideApproachStorage > 0 || !IsBridgedDrawer(__0)) { return true; } try { _insideApproachStorage++; _approachStorage.Invoke(__instance, new object[2] { __0, __2 }); return false; } catch (Exception e) { Plugin.Log.LogWarning((object)("[Drawer Movement] Chest-style drawer approach failed; using Innangard's direct approach: " + Bridge.Root(e))); return true; } finally { _insideApproachStorage--; } } private static bool IsBridgedDrawer(GameObject target) { if ((Object)(object)target == (Object)null) { return false; } if ((Object)(object)target.GetComponent<DrawerComponent>() != (Object)null) { return true; } if ((Object)(object)target.GetComponentInParent<DrawerComponent>() != (Object)null) { return true; } return (Object)(object)target.GetComponentInChildren<DrawerComponent>(true) != (Object)null; } } internal static class Bridge { private sealed class RecentRange { internal Vector3 Centre; internal float Range; internal float Seen; } private sealed class ReferenceComparer : IEqualityComparer<object> { internal static readonly ReferenceComparer Instance = new ReferenceComparer(); public new bool Equals(object x, object y) { return x == y; } public int GetHashCode(object obj) { return RuntimeHelpers.GetHashCode(obj); } } private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; internal static Type DrawerType; internal static PropertyInfo ItemNameProp; internal static PropertyInfo CountProp; internal static PropertyInfo FreeSpaceProp; internal static MethodInfo DepositMethod; internal static MethodInfo WithdrawMethod; internal static MemberInfo NviewMember; private const float MissingDrawerGraceSeconds = 2.5f; private const float RecoveryRefreshInterval = 0.25f; private static readonly Dictionary<object, DrawerComponent> ByReal = new Dictionary<object, DrawerComponent>(ReferenceComparer.Instance); private static readonly Dictionary<int, object> ByViewId = new Dictionary<int, object>(); private static readonly Dictionary<ZDOID, object> ByZdoId = new Dictionary<ZDOID, object>(); private static readonly Dictionary<ZDOID, DrawerComponent> ByZdoIdMarker = new Dictionary<ZDOID, DrawerComponent>(); private static readonly Dictionary<ZDOID, float> LastActiveSeen = new Dictionary<ZDOID, float>(); private static readonly List<RecentRange> RecentRanges = new List<RecentRange>(); private static float _nextRecoveryRefresh; private static int _lastFound = -1; private static int _lastActive = -1; internal static bool BindBetrValheim() { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "BetrValheim", StringComparison.OrdinalIgnoreCase)); if (assembly == null) { Plugin.Log.LogError((object)"[Bind] BetrValheim assembly not found."); return false; } DrawerType = assembly.GetType("BetrValheim.Drawers.Drawer", throwOnError: false); if (DrawerType == null) { Plugin.Log.LogError((object)"[Bind] BetrValheim.Drawers.Drawer not found."); return false; } ItemNameProp = DrawerType.GetProperty("ItemName", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); CountProp = DrawerType.GetProperty("Count", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); FreeSpaceProp = DrawerType.GetProperty("FreeSpace", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); DepositMethod = DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Deposit" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(string), typeof(int), typeof(bool))) ?? DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Deposit" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(string), typeof(int))); WithdrawMethod = DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Withdraw" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(int), typeof(bool))) ?? DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Withdraw" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(int))); NviewMember = (MemberInfo)(((object)DrawerType.GetField("_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)DrawerType.GetProperty("_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))); if (NviewMember == null) { NviewMember = (MemberInfo)(((object)DrawerType.GetField("m_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)DrawerType.GetProperty("m_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))); } Plugin.Log.LogInfo((object)("[Bind] BetrValheim drawer type: " + DrawerType.FullName)); Plugin.Log.LogInfo((object)("[Bind] ItemName: " + Describe(ItemNameProp))); Plugin.Log.LogInfo((object)("[Bind] Count: " + Describe(CountProp))); Plugin.Log.LogInfo((object)("[Bind] FreeSpace: " + Describe(FreeSpaceProp))); Plugin.Log.LogInfo((object)("[Bind] Deposit: " + Describe(DepositMethod))); Plugin.Log.LogInfo((object)("[Bind] Withdraw: " + Describe(WithdrawMethod))); Plugin.Log.LogInfo((object)("[Bind] ZNetView member: " + (NviewMember?.Name ?? "<missing>"))); int num; if (ItemNameProp != null && CountProp != null && FreeSpaceProp != null && DepositMethod != null && WithdrawMethod != null) { num = ((NviewMember != null) ? 1 : 0); if (num != 0) { goto IL_0332; } } else { num = 0; } Plugin.Log.LogError((object)"[Bind] One or more required BetrValheim members are missing."); goto IL_0332; IL_0332: return (byte)num != 0; } internal static void LogApiSurface() { try { Type? typeFromHandle = typeof(ItemDrawers_API); Type typeFromHandle2 = typeof(ItemDrawers_API.Drawer); MethodInfo method = typeFromHandle.GetMethod("AllDrawersInRange", BindingFlags.Static | BindingFlags.Public); MethodInfo method2 = typeFromHandle2.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); MethodInfo method3 = typeFromHandle2.GetMethod("Remove", BindingFlags.Instance | BindingFlags.Public); FieldInfo field = typeFromHandle2.GetField("Prefab", BindingFlags.Instance | BindingFlags.Public); FieldInfo field2 = typeFromHandle2.GetField("Amount", BindingFlags.Instance | BindingFlags.Public); FieldInfo field3 = typeFromHandle2.GetField("Quality", BindingFlags.Instance | BindingFlags.Public); FieldInfo fieldInfo = typeFromHandle2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => typeof(ZNetView).IsAssignableFrom(f.FieldType)); Plugin.Log.LogInfo((object)"[API] API.ItemDrawers_API, kg_ItemDrawers"); Plugin.Log.LogInfo((object)("[API] AllDrawersInRange(Vector3, Single): " + ((method != null && ParametersAre(method, typeof(Vector3), typeof(float))) ? "OK" : "MISSING/WRONG"))); Plugin.Log.LogInfo((object)("[API] Drawer.Add(Int32, Int32): " + ((method2 != null && ParametersAre(method2, typeof(int), typeof(int))) ? "OK" : "MISSING/WRONG"))); Plugin.Log.LogInfo((object)("[API] Drawer.Remove(Int32): " + ((method3 != null && ParametersAre(method3, typeof(int))) ? "OK" : "MISSING/WRONG"))); Plugin.Log.LogInfo((object)("[API] Drawer fields: Prefab=" + (field != null) + " Amount=" + (field2 != null) + " Quality=" + (field3 != null) + " ZNetView=" + (fieldInfo?.Name ?? "<missing>"))); Plugin.Log.LogInfo((object)"[API] ItemDrawers compatibility surface ready."); } catch (Exception e) { Plugin.Log.LogWarning((object)("[API] self-check failed: " + Root(e))); } } private static bool ParametersAre(MethodInfo method, params Type[] types) { if (method == null) { return false; } ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length != types.Length) { return false; } for (int i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType != types[i]) { return false; } } return true; } internal static void Refresh(bool forceSummary) { //IL_040c: Unknown result type (might be due to invalid IL or missing references) //IL_0419: Unknown result type (might be due to invalid IL or missing references) //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) if (DrawerType == null) { return; } Object[] array; try { array = Resources.FindObjectsOfTypeAll(DrawerType); } catch (Exception e) { Plugin.Log.LogWarning((object)("[Scan] FindObjectsOfTypeAll failed: " + Root(e))); return; } float unscaledTime = Time.unscaledTime; int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; Object[] array2 = array; foreach (Object val in array2) { if (val == (Object)null) { num++; continue; } Component val2 = (Component)(object)((val is Component) ? val : null); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.gameObject == (Object)null)) { Scene scene = val2.gameObject.scene; if (((Scene)(ref scene)).IsValid()) { object obj = val; ZNetView nview = GetNview(obj); if ((Object)(object)nview == (Object)null || !nview.IsValid()) { num3++; continue; } ZDO zDO = nview.GetZDO(); if (zDO == null || zDO.m_uid == ZDOID.None) { num3++; continue; } ZDOID uid = zDO.m_uid; GameObject val3 = PhysicalRoot(nview); if (!((Object)(object)val3 == (Object)null)) { scene = val3.scene; if (((Scene)(ref scene)).IsValid()) { bool flag = val2.gameObject.activeInHierarchy && val3.activeInHierarchy; bool flag2 = LastActiveSeen.ContainsKey(uid); if (!flag) { num2++; if (!flag2) { continue; } } else { LastActiveSeen[uid] = unscaledTime; } if (ByZdoId.TryGetValue(uid, out var value) && value != null && value != obj) { ByReal.Remove(value); } ByZdoId[uid] = obj; ByViewId[((Object)nview).GetInstanceID()] = obj; if (!ByZdoIdMarker.TryGetValue(uid, out var value2) || (Object)(object)value2 == (Object)null || (Object)(object)((Component)value2).gameObject != (Object)(object)val3) { if ((Object)(object)value2 != (Object)null) { DrawerComponent.AllDrawers.Remove(value2); try { Object.Destroy((Object)(object)value2); } catch { } } value2 = val3.GetComponent<DrawerComponent>(); if ((Object)(object)value2 == (Object)null) { value2 = val3.AddComponent<DrawerComponent>(); } ByZdoIdMarker[uid] = value2; } ByReal[obj] = value2; value2.Bind(obj, nview); value2.Sync(); if (!DrawerComponent.AllDrawers.Contains(value2)) { DrawerComponent.AllDrawers.Add(value2); } string assigned = GetAssigned(obj); if (string.IsNullOrEmpty(assigned)) { num5++; } else { num4++; } if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)("[Drawer] root='" + ((Object)val3).name + "' real='" + ((Object)val2.gameObject).name + "' " + $"pos={val3.transform.position} assigned='{assigned}' prefab='{value2.CurrentPrefab}' " + $"count={GetCount(obj)} free={GetFreeSpace(obj)} zdo={uid} active={flag}")); } continue; } } num2++; continue; } } num2++; } ZDOID[] array3 = LastActiveSeen.Keys.ToArray(); foreach (ZDOID key in array3) { float num6 = LastActiveSeen[key]; if (unscaledTime - num6 <= 2.5f) { continue; } if (ByZdoId.TryGetValue(key, out var value3) && value3 != null) { ByReal.Remove(value3); } if (ByZdoIdMarker.TryGetValue(key, out var value4) && (Object)(object)value4 != (Object)null) { DrawerComponent.AllDrawers.Remove(value4); try { Object.Destroy((Object)(object)value4); } catch { } } ByZdoId.Remove(key); ByZdoIdMarker.Remove(key); LastActiveSeen.Remove(key); } KeyValuePair<int, object>[] array4 = ByViewId.ToArray(); for (int i = 0; i < array4.Length; i++) { KeyValuePair<int, object> keyValuePair = array4[i]; ZNetView nview2 = GetNview(keyValuePair.Value); if ((Object)(object)nview2 == (Object)null || !nview2.IsValid() || ((Object)nview2).GetInstanceID() != keyValuePair.Key) { ByViewId.Remove(keyValuePair.Key); } } DrawerComponent.AllDrawers.RemoveAll(delegate(DrawerComponent d) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)d == (Object)null) && !((Object)(object)d.View == (Object)null) && d.View.IsValid() && d.View.GetZDO() != null && !((Object)(object)((Component)d).gameObject == (Object)null)) { Scene scene2 = ((Component)d).gameObject.scene; return !((Scene)(ref scene2)).IsValid(); } return true; }); int num7 = 0; int num8 = 0; foreach (DrawerComponent allDrawer in DrawerComponent.AllDrawers) { if (!((Object)(object)allDrawer == (Object)null) && !((Object)(object)((Component)allDrawer).gameObject == (Object)null)) { if (((Component)allDrawer).gameObject.activeInHierarchy) { num7++; } else { num8++; } } } int count = DrawerComponent.AllDrawers.Count; if (forceSummary || array.Length != _lastFound || count != _lastActive) { _lastFound = array.Length; _lastActive = count; Plugin.Log.LogInfo((object)($"[Scan] found={array.Length} cached={count} active={num7} grace={num8} " + $"assigned={num4} unassigned={num5} invalidNview={num3} inactive={num2} null={num}")); } } internal static void Clear() { DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray(); foreach (DrawerComponent drawerComponent in array) { if ((Object)(object)drawerComponent != (Object)null) { try { Object.Destroy((Object)(object)drawerComponent); } catch { } } } DrawerComponent.AllDrawers.Clear(); ByReal.Clear(); ByViewId.Clear(); ByZdoId.Clear(); ByZdoIdMarker.Clear(); LastActiveSeen.Clear(); RecentRanges.Clear(); _nextRecoveryRefresh = 0f; _lastFound = -1; _lastActive = -1; } internal static GameObject PhysicalRoot(ZNetView view) { if ((Object)(object)view == (Object)null) { return null; } Piece val = ((Component)view).GetComponent<Piece>() ?? ((Component)view).GetComponentInParent<Piece>(); if (!((Object)(object)val != (Object)null)) { return ((Component)view).gameObject; } return ((Component)val).gameObject; } internal static ZNetView GetNview(object real) { try { if (real == null || NviewMember == null) { return null; } if (NviewMember is FieldInfo fieldInfo) { object? value = fieldInfo.GetValue(real); return (ZNetView)((value is ZNetView) ? value : null); } if (NviewMember is PropertyInfo propertyInfo) { object? value2 = propertyInfo.GetValue(real, null); return (ZNetView)((value2 is ZNetView) ? value2 : null); } } catch (Exception e) { Plugin.Log.LogWarning((object)("[ZNetView] " + Root(e))); } return null; } private static bool TryGetZdoId(ZNetView view, out ZDOID zdoId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) zdoId = ZDOID.None; if ((Object)(object)view == (Object)null || !view.IsValid()) { return false; } ZDO zDO = view.GetZDO(); if (zDO == null || zDO.m_uid == ZDOID.None) { return false; } zdoId = zDO.m_uid; return true; } internal static bool TryGetReal(ZNetView view, out object real) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) real = null; if ((Object)(object)view == (Object)null) { return false; } if (TryGetZdoId(view, out var zdoId) && ByZdoId.TryGetValue(zdoId, out real) && real != null && TryGetZdoId(GetNview(real), out var zdoId2) && zdoId2 == zdoId) { return true; } if (ByViewId.TryGetValue(((Object)view).GetInstanceID(), out real) && real != null) { ZNetView nview = GetNview(real); if ((Object)(object)nview != (Object)null && (Object)(object)nview == (Object)(object)view && nview.IsValid()) { return true; } } real = null; return false; } private static bool MarkerUsable(DrawerComponent marker, float now) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)marker == (Object)null) && !((Object)(object)marker.View == (Object)null) && marker.View.IsValid() && marker.View.GetZDO() != null && !((Object)(object)((Component)marker).gameObject == (Object)null)) { Scene scene = ((Component)marker).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { if (((Component)marker).gameObject.activeInHierarchy) { return true; } if (TryGetZdoId(marker.View, out var zdoId) && LastActiveSeen.TryGetValue(zdoId, out var value)) { return now - value <= 2.5f; } return false; } } return false; } internal static List<ZNetView> AllViews() { float unscaledTime = Time.unscaledTime; List<ZNetView> list = new List<ZNetView>(); DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray(); foreach (DrawerComponent drawerComponent in array) { if (MarkerUsable(drawerComponent, unscaledTime)) { drawerComponent.Sync(); list.Add(drawerComponent.View); } } return list; } internal static void RememberRange(Vector3 centre, float range) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (range <= 0f) { return; } float now = Time.unscaledTime; RecentRanges.RemoveAll((RecentRange q) => q == null || now - q.Seen > 3f); RecentRange recentRange = RecentRanges.FirstOrDefault((RecentRange q) => Vector3.Distance(q.Centre, centre) < 1f && Mathf.Abs(q.Range - range) < 1f); if (recentRange != null) { recentRange.Centre = centre; recentRange.Range = range; recentRange.Seen = now; return; } RecentRanges.Add(new RecentRange { Centre = centre, Range = range, Seen = now }); while (RecentRanges.Count > 8) { RecentRange recentRange2 = RecentRanges.OrderBy((RecentRange q) => q.Seen).FirstOrDefault(); if (recentRange2 != null) { RecentRanges.Remove(recentRange2); continue; } break; } } private static bool TryRelevantRange(Vector3 chestPosition, out Vector3 centre, out float range) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) centre = Vector3.zero; range = 0f; float unscaledTime = Time.unscaledTime; RecentRange recentRange = null; float num = float.MaxValue; foreach (RecentRange recentRange2 in RecentRanges) { if (recentRange2 != null && !(unscaledTime - recentRange2.Seen > 2f)) { float num2 = Vector3.Distance(chestPosition, recentRange2.Centre); if (!(num2 > recentRange2.Range + 10f) && !(num2 >= num)) { recentRange = recentRange2; num = num2; } } } if (recentRange == null) { return false; } centre = recentRange.Centre; range = recentRange.Range; return true; } private static bool MatchingDrawerInRange(string sharedName, Vector3 centre, float range) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) string text = ResolvePrefabName(sharedName); if (string.IsNullOrEmpty(text)) { return false; } float unscaledTime = Time.unscaledTime; float num = range * range; DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray(); foreach (DrawerComponent drawerComponent in array) { if (MarkerUsable(drawerComponent, unscaledTime) && drawerComponent.RealDrawer != null) { Vector3 val = ((Component)drawerComponent.View).transform.position - centre; if (!(((Vector3)(ref val)).sqrMagnitude > num) && GetFreeSpace(drawerComponent.RealDrawer) > 0 && (Matches(drawerComponent.CurrentPrefab, text) || Matches(GetAssigned(drawerComponent.RealDrawer), sharedName))) { return true; } } } return false; } internal static bool MatchingDrawerBeforeBo(Container chest, string sharedName) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest == (Object)null || string.IsNullOrEmpty(sharedName)) { return false; } if (!TryRelevantRange(((Component)chest).transform.position, out var centre, out var range)) { return false; } if (MatchingDrawerInRange(sharedName, centre, range)) { return true; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextRecoveryRefresh) { return false; } _nextRecoveryRefresh = unscaledTime + 0.25f; Refresh(forceSummary: false); bool num = MatchingDrawerInRange(sharedName, centre, range); if (num) { Plugin.Log.LogInfo((object)("[Drawer] Recovered matching drawer for '" + sharedName + "' before Better Organisation fallback.")); } return num; } internal static void SyncOne(object real) { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_007c: Unknown result type (might be due to invalid IL or missing references) if (real == null) { return; } if (ByReal.TryGetValue(real, out var value) && (Object)(object)value != (Object)null) { if (TryGetZdoId(value.View, out var zdoId)) { LastActiveSeen[zdoId] = Time.unscaledTime; } value.Sync(); return; } ZNetView nview = GetNview(real); if (!TryGetZdoId(nview, out var zdoId2)) { return; } GameObject val = PhysicalRoot(nview); if ((Object)(object)val == (Object)null) { return; } Scene scene = val.scene; if (!((Scene)(ref scene)).IsValid()) { return; } if (ByZdoIdMarker.TryGetValue(zdoId2, out var value2) && (Object)(object)value2 != (Object)null && (Object)(object)((Component)value2).gameObject != (Object)(object)val) { DrawerComponent.AllDrawers.Remove(value2); try { Object.Destroy((Object)(object)value2); } catch { } } value = val.GetComponent<DrawerComponent>(); if ((Object)(object)value == (Object)null) { value = val.AddComponent<DrawerComponent>(); } value.Bind(real, nview); value.Sync(); if (ByZdoId.TryGetValue(zdoId2, out var value3) && value3 != null && value3 != real) { ByReal.Remove(value3); } ByReal[real] = value; ByZdoId[zdoId2] = real; ByZdoIdMarker[zdoId2] = value; ByViewId[((Object)nview).GetInstanceID()] = real; LastActiveSeen[zdoId2] = Time.unscaledTime; if (!DrawerComponent.AllDrawers.Contains(value)) { DrawerComponent.AllDrawers.Add(value); } } internal static string GetAssigned(object real) { try { return (ItemNameProp?.GetValue(real, null) as string) ?? string.Empty; } catch { return string.Empty; } } internal static int GetCount(object real) { try { object obj = CountProp?.GetValue(real, null); return (obj != null) ? Convert.ToInt32(obj) : 0; } catch { return 0; } } internal static int GetFreeSpace(object real) { try { object obj = FreeSpaceProp?.GetValue(real, null); return (obj != null) ? Convert.ToInt32(obj) : 0; } catch { return 0; } } internal static string ResolvePrefabName(string assigned) { if (string.IsNullOrEmpty(assigned)) { return string.Empty; } try { if ((Object)(object)ObjectDB.instance != (Object)null) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(assigned); if ((Object)(object)itemPrefab != (Object)null) { return ((Object)itemPrefab).name; } foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData?.m_shared != null) { string name = component.m_itemData.m_shared.m_name; if (string.Equals(name, assigned, StringComparison.Ordinal) || string.Equals(name?.TrimStart('$'), assigned?.TrimStart('$'), StringComparison.OrdinalIgnoreCase)) { return ((Object)item).name; } } } } } catch (Exception e) { Plugin.Log.LogWarning((object)("[Prefab] could not resolve '" + assigned + "': " + Root(e))); } return assigned; } private static object InvokeDeposit(object real, string prefab, int amount) { if (DepositMethod.GetParameters().Length != 3) { return DepositMethod.Invoke(real, new object[2] { prefab, amount }); } return DepositMethod.Invoke(real, new object[3] { prefab, amount, true }); } private static object InvokeWithdraw(object real, int amount) { if (WithdrawMethod.GetParameters().Length != 2) { return WithdrawMethod.Invoke(real, new object[1] { amount }); } return WithdrawMethod.Invoke(real, new object[2] { amount, true }); } internal static void ApiAdd(ZNetView view, string snapshotPrefab, int amount, int quality) { if ((Object)(object)view == (Object)null || !view.IsValid()) { throw new InvalidOperationException("ItemDrawers API Add: drawer ZNetView is invalid."); } if (amount <= 0) { throw new ArgumentOutOfRangeException("amount"); } if (quality <= 0) { quality = 1; } if (!TryGetReal(view, out var real)) { throw new InvalidOperationException("ItemDrawers API Add: backing BetrValheim drawer was not found."); } string text = ResolvePrefabName(GetAssigned(real)); if (string.IsNullOrEmpty(text)) { throw new InvalidOperationException("ItemDrawers API Add: backing drawer is unassigned."); } if (!string.IsNullOrEmpty(snapshotPrefab) && !Matches(text, snapshotPrefab)) { throw new InvalidOperationException("ItemDrawers API Add: drawer changed from '" + snapshotPrefab + "' to '" + text + "'."); } if (quality != 1) { throw new InvalidOperationException($"ItemDrawers API Add: BetrValheim drawer cannot represent quality {quality}."); } int num = Math.Max(0, GetFreeSpace(real)); if (num < amount) { throw new InvalidOperationException($"ItemDrawers API Add: requested {amount}, free space is {num}."); } if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[ADD->] prefab='{text}' amount={amount} quality={quality} count={GetCount(real)} free={num}"); } object obj = InvokeDeposit(real, text, amount); int num2 = ((obj != null) ? Convert.ToInt32(obj) : 0); if (num2 != amount) { if (num2 > 0) { try { InvokeWithdraw(real, num2); } catch (Exception e) { Plugin.Log.LogError((object)("[ADD] partial-deposit rollback failed: " + Root(e))); } } SyncOne(real); throw new InvalidOperationException($"ItemDrawers API Add: BetrValheim moved {num2}, expected {amount}."); } SyncOne(real); if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[ADD<-] prefab='{text}' moved={num2} count={GetCount(real)} free={GetFreeSpace(real)}"); } } internal static void ApiRemove(ZNetView view, int amount) { if ((Object)(object)view == (Object)null || !view.IsValid()) { throw new InvalidOperationException("ItemDrawers API Remove: drawer ZNetView is invalid."); } if (amount <= 0) { throw new ArgumentOutOfRangeException("amount"); } if (!TryGetReal(view, out var real)) { throw new InvalidOperationException("ItemDrawers API Remove: backing BetrValheim drawer was not found."); } int num = Math.Max(0, GetCount(real)); if (num < amount) { throw new InvalidOperationException($"ItemDrawers API Remove: requested {amount}, only {num} available."); } string text = ResolvePrefabName(GetAssigned(real)); if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[REMOVE->] prefab='{text}' amount={amount} count={num}"); } object obj = InvokeWithdraw(real, amount); int num2 = ((obj != null) ? Convert.ToInt32(obj) : 0); if (num2 != amount) { if (num2 > 0 && !string.IsNullOrEmpty(text)) { try { InvokeDeposit(real, text, num2); } catch (Exception e) { Plugin.Log.LogError((object)("[REMOVE] partial-withdraw rollback failed: " + Root(e))); } } SyncOne(real); throw new InvalidOperationException($"ItemDrawers API Remove: BetrValheim moved {num2}, expected {amount}."); } SyncOne(real); if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[REMOVE<-] prefab='{text}' moved={num2} count={GetCount(real)}"); } } internal static bool Matches(string left, string right) { if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right)) { return false; } if (string.Equals(left, right, StringComparison.OrdinalIgnoreCase)) { return true; } return string.Equals(left.TrimStart('$').Replace("_", string.Empty), right.TrimStart('$').Replace("_", string.Empty), StringComparison.OrdinalIgnoreCase); } internal static string Describe(PropertyInfo p) { if (!(p == null)) { return p.DeclaringType?.FullName + "." + p.Name + " : " + p.PropertyType.FullName; } return "<missing>"; } internal static string Describe(MethodInfo m) { if (m == null) { return "<missing>"; } return m.DeclaringType?.FullName + "." + m.Name + "(" + string.Join(", ", from p in m.GetParameters() select p.ParameterType.Name) + ") -> " + m.ReturnType.Name; } internal static string Root(Exception e) { while (e is TargetInvocationException { InnerException: not null } ex) { e = ex.InnerException; } return e?.Message ?? "<unknown>"; } } internal static class BetterOrganisationChestPanelCompat { private const BindingFlags InstanceAny = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private const BindingFlags StaticAny = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const string CompatObjectName = "pho_bo_compat_note"; private const string CompatButtonObjectName = "pho_bo_compat_button"; private const string CompatText = "Override: Storage Rules managed by Better Organisation"; private const string CompatButtonText = "Open Better Organisation"; [ThreadStatic] private static bool _inPanelRead; private static bool _installed; private static bool _reportedMissing; private static bool _reportedSuppression; private static FieldInfo _container; private static FieldInfo _picking; private static FieldInfo _column; private static FieldInfo _grid; private static FieldInfo _choices; private static FieldInfo _mineHelp; private static FieldInfo _origin; private static FieldInfo _byHand; private static FieldInfo _slack; private static FieldInfo _remeasureFlag; private static MethodInfo _remeasure; private static MethodInfo _resize; private static PropertyInfo _boUiInstance; private static PropertyInfo _boGui; private static MethodInfo _boToggleEditor; internal static bool InPanelRead => _inPanelRead; internal static bool Install(Harmony harmony) { //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Expected O, but got Unknown //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Expected O, but got Unknown //IL_03f9: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Expected O, but got Unknown //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Expected O, but got Unknown //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_0463: Expected O, but got Unknown if (_installed) { return true; } if (!BetterOrganisationRules.IsAvailable) { return false; } Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase)); Type type = assembly?.GetType("Innangard.ChestSortPanel", throwOnError: false); if (type == null) { if (!_reportedMissing && assembly != null) { _reportedMissing = true; Plugin.Log.LogWarning((object)"[BO UI] Innangard.ChestSortPanel was not found; Shift+E picker suppression is unavailable."); } return false; } MethodInfo method = type.GetMethod("Open", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Container) }, null); MethodInfo method2 = type.GetMethod("Refresh", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m) { if (m.Name != "BuildChoices") { return false; } ParameterInfo[] parameters = m.GetParameters(); return parameters.Length == 2 && typeof(Container).IsAssignableFrom(parameters[0].ParameterType); }); MethodInfo methodInfo2 = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Populate" && m.GetParameters().Length == 1 && m.ReturnType == typeof(void)); _container = type.GetField("_container", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _picking = type.GetField("_picking", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _column = type.GetField("_column", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _grid = type.GetField("_grid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _choices = type.GetField("_choices", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _mineHelp = type.GetField("_mineHelp", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _origin = type.GetField("_origin", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _byHand = type.GetField("_byHand", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _slack = type.GetField("_slack", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _remeasureFlag = type.GetField("_remeasure", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _remeasure = type.GetMethod("Remeasure", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); _resize = type.GetMethod("Resize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); Type? obj = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase))?.GetType("ChestCategories.UI.ModUi", throwOnError: false); _boUiInstance = obj?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _boGui = obj?.GetProperty("Gui", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _boToggleEditor = obj?.GetMethod("ToggleEditor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Container) }, null); if (method == null || method2 == null || methodInfo == null || methodInfo2 == null || _container == null || _picking == null || _column == null || _mineHelp == null) { if (!_reportedMissing) { _reportedMissing = true; Plugin.Log.LogWarning((object)("[BO UI] Innangard 0.1.9 Shift+E panel binding incomplete: " + $"Open={method != null} Refresh={method2 != null} BuildChoices={methodInfo != null} " + $"Populate={methodInfo2 != null} container={_container != null} picking={_picking != null} column={_column != null} mineHelp={_mineHelp != null}.")); } return false; } HarmonyMethod val = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopePrefix", (Type[])null); HarmonyMethod val2 = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopePostfix", (Type[])null); HarmonyMethod val3 = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopeFinalizer", (Type[])null); harmony.Patch((MethodBase)method, val, val2, (HarmonyMethod)null, val3, (HarmonyMethod)null); harmony.Patch((MethodBase)method2, val, val2, (HarmonyMethod)null, val3, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "BuildChoicesPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PopulatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _installed = true; Plugin.Log.LogInfo((object)"[BO UI] Bound Innangard.ChestSortPanel Open/Refresh/BuildChoices/Populate."); Plugin.Log.LogInfo((object)"[BO UI] BO-managed Shift+E panels keep intake/private controls and replace only the material picker with a Better Organisation storage-rules shortcut."); if (_boUiInstance != null && _boToggleEditor != null) { Plugin.Log.LogInfo((object)"[BO UI] Better Organisation category editor shortcut bound."); } else { Plugin.Log.LogWarning((object)"[BO UI] Better Organisation category editor shortcut was not found; the compatibility heading will still be shown."); } return true; } private static void PanelScopePrefix() { _inPanelRead = true; } private static void PanelScopePostfix(object __instance) { _inPanelRead = false; Apply(__instance); } private static Exception PanelScopeFinalizer(Exception __exception) { _inPanelRead = false; return __exception; } private static bool BuildChoicesPrefix(Container __0) { return !BetterOrganisationRules.IsManaged(__0); } private static bool PopulatePrefix(object __instance) { if (!BetterOrganisationRules.IsManaged(GetContainer(__instance))) { return true; } ClearPickerRows(__instance); return false; } private static Container GetContainer(object panel) { try { return (Container)((panel != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private static void ClearPickerRows(object panel) { try { object? obj = _grid?.GetValue(panel); RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null); if ((Object)(object)val != (Object)null) { for (int num = ((Transform)val).childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)((Transform)val).GetChild(num)).gameObject); } } (_choices?.GetValue(panel) as IList)?.Clear(); } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO UI] Could not clear Innangard's old material rows: " + Bridge.Root(e))); } } private static void Apply(object panel) { if (panel == null) { return; } try { Container container = GetContainer(panel); bool flag = BetterOrganisationRules.IsManaged(container); object? value = _picking.GetValue(panel); RectTransform val = (RectTransform)((value is RectTransform) ? value : null); object? value2 = _column.GetValue(panel); RectTransform val2 = (RectTransform)((value2 is RectTransform) ? value2 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return; } GameObject val3 = FindCompatObject(val2, "pho_bo_compat_note"); GameObject val4 = FindCompatObject(val2, "pho_bo_compat_button"); bool flag2 = false; if (flag) { if (((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); flag2 = true; } if ((Object)(object)val3 == (Object)null) { val3 = CreateCompatNote(panel, val2, val); flag2 = (Object)(object)val3 != (Object)null; } if ((Object)(object)val4 == (Object)null && _boUiInstance != null && _boToggleEditor != null) { val4 = CreateCompatButton(panel, val2, val, container); flag2 = (Object)(object)val4 != (Object)null; } if ((Object)(object)val3 != (Object)null) { if (!val3.activeSelf) { val3.SetActive(true); flag2 = true; } int siblingIndex = ((Transform)val).GetSiblingIndex(); if (val3.transform.GetSiblingIndex() != siblingIndex) { val3.transform.SetSiblingIndex(siblingIndex); flag2 = true; } } if ((Object)(object)val4 != (Object)null) { BetterOrganisationEditorButtonAction component = val4.GetComponent<BetterOrganisationEditorButtonAction>(); if ((Object)(object)component != (Object)null) { component.Target = container; component.InnangardPanel = panel; } if (!val4.activeSelf) { val4.SetActive(true); flag2 = true; } int num = (((Object)(object)val3 != (Object)null) ? Math.Min(val3.transform.GetSiblingIndex() + 1, Math.Max(0, ((Transform)val2).childCount - 1)) : ((Transform)val).GetSiblingIndex()); if (val4.transform.GetSiblingIndex() != num) { val4.transform.SetSiblingIndex(num); flag2 = true; } } ClearPickerRows(panel); SetSlackFlexible(panel, 1f); if (!_reportedSuppression) { _reportedSuppression = true; Plugin.Log.LogInfo((object)"[BO UI] Suppressed the Shift+E Search/material list for a Better Organisation-managed chest."); } } else { if ((Object)(object)val3 != (Object)null && val3.activeSelf) { val3.SetActive(false); flag2 = true; } if ((Object)(object)val4 != (Object)null && val4.activeSelf) { val4.SetActive(false); flag2 = true; } } if (flag2) { if (_remeasureFlag != null) { _remeasureFlag.SetValue(panel, true); } _remeasure?.Invoke(panel, null); _resize?.Invoke(panel, null); } } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO UI] Could not apply Shift+E material-picker compatibility layout: " + Bridge.Root(e))); } } private static GameObject FindCompatObject(RectTransform column, string name) { for (int i = 0; i < ((Transform)column).childCount; i++) { Transform child = ((Transform)column).GetChild(i); if ((Object)(object)child != (Object)null && (Object)(object)((Component)child).gameObject != (Object)null && ((Object)((Component)child).gameObject).name == name) { return ((Component)child).gameObject; } } return null; } private static GameObject CreateCompatNote(object panel, RectTransform column, RectTransform picking) { object? obj = _origin?.GetValue(panel); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null) { object? obj2 = _mineHelp?.GetValue(panel); val = (Component)((obj2 is Component) ? obj2 : null); } if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null) { return null; } GameObject obj3 = Object.Instantiate<GameObject>(val.gameObject); ((Object)obj3).name = "pho_bo_compat_note"; obj3.transform.SetParent((Transform)(object)column, false); obj3.transform.SetSiblingIndex(((Transform)picking).GetSiblingIndex()); Component component = obj3.GetComponent(((object)val).GetType()); SetText(component, "Override: Storage Rules managed by Better Organisation"); SetNumericProperty(component, "fontSize", 20); obj3.SetActive(true); return obj3; } private static GameObject CreateCompatButton(object panel, RectTransform column, RectTransform picking, Container container) { object? obj = _byHand?.GetValue(panel); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null) { return null; } GameObject val2 = Object.Instantiate<GameObject>(val.gameObject); ((Object)val2).name = "pho_bo_compat_button"; val2.transform.SetParent((Transform)(object)column, false); val2.transform.SetSiblingIndex(((Transform)picking).GetSiblingIndex()); SetFirstChildText(val2, "Open Better Organisation"); Component component = val2.GetComponent(((object)val).GetType()); object obj2 = ((object)component)?.GetType().GetProperty("onClick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(component, null); if (obj2 == null) { Object.Destroy((Object)(object)val2); return null; } obj2.GetType().GetMethod("RemoveAllListeners", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)?.Invoke(obj2, null); BetterOrganisationEditorButtonAction betterOrganisationEditorButtonAction = val2.AddComponent<BetterOrganisationEditorButtonAction>(); betterOrganisationEditorButtonAction.Target = container; betterOrganisationEditorButtonAction.InnangardPanel = panel; MethodInfo method = typeof(BetterOrganisationEditorButtonAction).GetMethod("Click", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo methodInfo = obj2.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddListener" && m.GetParameters().Length == 1); if (method == null || methodInfo == null) { Object.Destroy((Object)(object)val2); return null; } Delegate obj3 = Delegate.CreateDelegate(methodInfo.GetParameters()[0].ParameterType, betterOrganisationEditorButtonAction, method); methodInfo.Invoke(obj2, new object[1] { obj3 }); val2.SetActive(true); return val2; } private static void SetFirstChildText(GameObject root, string value) { if (!((Object)(object)root == (Object)null)) { Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true); for (int i = 0; i < componentsInChildren.Length && !SetText(componentsInChildren[i], value); i++) { } } } private static bool SetText(Component component, string value) { if ((Object)(object)component == (Object)null) { return false; } try { PropertyInfo property = ((object)component).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null || !property.CanWrite || property.PropertyType != typeof(string)) { return false; } property.SetValue(component, value, null); return true; } catch { return false; } } private static void SetNumericProperty(Component component, string name, int value) { if ((Object)(object)component == (Object)null) { return; } try { PropertyInfo property = ((object)component).GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(property == null) && property.CanWrite) { if (property.PropertyType == typeof(int)) { property.SetValue(component, value, null); } else if (property.PropertyType == typeof(float)) { property.SetValue(component, (float)value, null); } } } catch { } } internal static void CloseInnangardPanel(object panel) { if (panel == null) { return; } try { Type type = panel.GetType(); MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => (m.Name == "Close" || m.Name == "Hide" || m.Name == "Dismiss") && m.GetParameters().Length == 0 && m.ReturnType == typeof(void)); if (methodInfo != null) { methodInfo.Invoke(panel, null); Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation."); return; } string[] array = new string[4] { "m_panel", "_panel", "_root", "m_root" }; foreach (string name in array) { object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(panel); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (val != null) { val.SetActive(false); Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation."); return; } Component val2 = (Component)((obj is Component) ? obj : null); if (val2 != null && (Object)(object)val2.gameObject != (Object)null) { val2.gameObject.SetActive(false); Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation."); return; } } Component val3 = (Component)((panel is Component) ? panel : null); if (val3 != null && (Object)(object)val3.gameObject != (Object)null) { val3.gameObject.SetActive(false); Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation."); } else { Plugin.Log.LogWarning((object)"[BO UI] Could not find Innangard's panel root to close it."); } } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO UI] Could not close Innangard Shift+E panel: " + Bridge.Root(e))); } } internal static void OpenBetterOrganisationEditor(Container container) { if ((Object)(object)container == (Object)null || _boUiInstance == null || _boToggleEditor == null) { return; } try { object value = _boUiInstance.GetValue(null, null); if (value == null) { Plugin.Log.LogWarning((object)"[BO UI] Better Organisation UI is not ready yet."); return; } bool flag = false; MethodInfo method = typeof(InventoryGui).GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { flag = (bool)method.Invoke(null, null); } if (!flag) { object obj = _boGui?.GetValue(value, null); (obj?.GetType().GetMethod("Show", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(Container), typeof(int) }, null))?.Invoke(obj, new object[2] { null, 1 }); } _boToggleEditor.Invoke(value, new object[1] { container }); Plugin.Log.LogInfo((object)"[BO UI] Opened Better Organisation storage rules for the current chest."); } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO UI] Could not open Better Organisation storage rules: " + Bridge.Root(e))); } } private static void SetSlackFlexible(object panel, float value) { try { object obj = _slack?.GetValue(panel); (obj?.GetType().GetProperty("flexibleHeight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.SetValue(obj, value, null); } catch { } } } internal sealed class BetterOrganisationEditorButtonAction : MonoBehaviour { internal Container Target; internal object InnangardPanel; public void Click() { BetterOrganisationChestPanelCompat.CloseInnangardPanel(InnangardPanel); BetterOrganisationChestPanelCompat.OpenBetterOrganisationEditor(Target); } } internal static class BetterOrganisationRules { private sealed class RuleCache { internal string Signature; internal bool HasAnyRule; internal HashSet<string> Accepted; } private sealed class RuleSnapshot { internal bool HasLinks; internal bool AcceptsEverything; internal bool AcceptsStash = true; internal readonly HashSet<string> Include = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal readonly HashSet<string> Exclude = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal bool HasAnyRule { get { if (!HasLinks && !AcceptsEverything && AcceptsStash && Include.Count <= 0) { return Exclude.Count > 0; } return true; } } } private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const string MirrorKey = "pho_bo_inn_mirror"; private const string BackupConfiguredKey = "pho_bo_inn_b_cfg"; private const string BackupKindsKey = "pho_bo_inn_b_kinds"; private const string BackupItemsKey = "pho_bo_inn_b_items"; private const string BackupLegacyKindKey = "pho_bo_inn_b_legacy"; private static Assembly _assembly; private static Type _storeType; private static Type _configType; private static Type _categoryServiceType; private static Type _categoryType; private static Type _groupType; private static MethodInfo _getConfig; private static MethodInfo _getCategory; private static PropertyInfo _hasLinks; private static PropertyInfo _links; private static PropertyInfo _includeItems; private static PropertyInfo _excludeItems; private static PropertyInfo _acceptsEverything; private static PropertyInfo _acceptsStash; private static MethodInfo _isLinkedCategory; private static MethodInfo _isLinkedGroup; private static PropertyInfo _categoryGroup; private static PropertyInfo _categoryId; private static PropertyInfo _groupId; private static MethodInfo _serializeConfig; private static readonly Dictionary<int, RuleCache> RuleCaches = new Dictionary<int, RuleCache>(); private static bool _bound; private static bool _reportedUnavailable; private static bool _installed; private static bool _reportedMissing; private static int _lastActive = -1; internal static bool IsAvailable => _bound; internal static bool Install(Harmony harmony) { //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Expected O, but got Unknown //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Expected O, but got Unknown //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Expected O, but got Unknown if (!_bound) { return false; } if (_installed) { return true; } Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.ChestSortRules", throwOnError: false); if (type == null) { return false; } BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; Type[] types = new Type[1] { typeof(Container) }; MethodInfo method = type.GetMethod("IsConfigured", bindingAttr, null, types, null); MethodInfo method2 = type.GetMethod("KindsOf", bindingAttr, null, types, null); MethodInfo method3 = type.GetMethod("ReadItems", bindingAttr, null, types, null); Type[] types2 = new Type[2] { typeof(Container), typeof(string) }; MethodInfo method4 = type.GetMethod("Accepts", bindingAttr, null, types2, null); MethodInfo method5 = type.GetMethod("Precision", bindingAttr, null, types2, null); if (method == null || method.ReturnType != typeof(bool) || method2 == null || method2.ReturnType != typeof(int) || method3 == null || method3.ReturnType != typeof(HashSet<string>) || method4 == null || method4.ReturnType != typeof(bool) || method5 == null || method5.ReturnType != typeof(int)) { if (!_reportedMissing) { _reportedMissing = true; Plugin.Log.LogError((object)"[BO] Innangard chest rule methods changed; local BO matching is unavailable."); } return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(BetterOrganisationRules), "ConfiguredPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method2, new HarmonyMethod(typeof(BetterOrganisationRules), "KindsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method3, new HarmonyMethod(typeof(BetterOrganisationRules), "ItemsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method4, new HarmonyMethod(typeof(BetterOrganisationRules), "AcceptsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method5, new HarmonyMethod(typeof(BetterOrganisationRules), "PrecisionPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _installed = true; Plugin.Log.LogInfo((object)"[BO] Innangard now reads BO category/item rules locally; no extra rule list is written to chests."); return true; } private static bool ConfiguredPrefix(Container __0, ref bool __result) { if (!TryGetAccepted(__0, out var _)) { return true; } __result = true; return false; } private static bool KindsPrefix(Container __0, ref int __result) { if (!TryGetAccepted(__0, out var _)) { return true; } __result = 0; return false; } private static bool ItemsPrefix(Container __0, ref HashSet<string> __result) { if (!TryGetAccepted(__0, out var accepted)) { return true; } if (BetterOrganisationChestPanelCompat.InPanelRead) { __result = new HashSet<string>(StringComparer.Ordinal) { "__pho_bo_managed__" }; return false; } __result = new HashSet<string>(accepted, StringComparer.Ordinal); return false; } private static bool AcceptsPrefix(Container __0, string __1, ref bool __result) { if (!TryGetAccepted(__0, out var accepted)) { return true; } __result = !string.IsNullOrEmpty(__1) && accepted.Contains(__1); return false; } private static bool PrecisionPrefix(Container __0, string __1, ref int __result) { if (!TryGetAccepted(__0, out var accepted)) { return true; } if (string.IsNullOrEmpty(__1) || !accepted.Contains(__1)) { __result = int.MaxValue; return false; } if (Bridge.MatchingDrawerBeforeBo(__0, __1)) { __result = int.MaxValue; return false; } __result = 0; return false; } internal static bool IsManaged(Container container) { HashSet<string> accepted; return TryGetAccepted(container, out accepted); } private static bool TryGetAccepted(Container container, out HashSet<string> accepted) { accepted = null; if (!_installed || !_bound || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)container == (Object)null) { return false; } ZNetView containerView = GetContainerView(container); if ((Object)(object)containerView == (Object)null || !containerView.IsValid() || containerView.GetZDO() == null) { return false; } int instanceID = ((Object)containerView).GetInstanceID(); if (!RuleCaches.TryGetValue(instanceID, out var value)) { value = RefreshCache(container, containerView); } if (value == null || !value.HasAnyRule) { return false; } accepted = value.Accepted; return true; } internal static void Bind() { _assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase)); if (_assembly == null) { Plugin.Log.LogInfo((object)"[BO] Better Organisation not loaded; Innangard chest rules are untouched."); return; } _storeType = _assembly.GetType("ChestCategories.Chests.ChestConfigStore", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "ChestConfigStore"); _configType = _assembly.GetType("ChestCategories.Chests.ChestConfig", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "ChestConfig"); _categoryServiceType = _assembly.GetType("ChestCategories.Categories.CategoryService", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "CategoryService"); _categoryType = _assembly.GetType("ChestCategories.Categories.Category", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "Category"); _groupType = _assembly.GetType("ChestCategories.Categories.CategoryGroup", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "CategoryGroup"); _getConfig = _storeType?.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Get" && m.IsStatic && m.GetParameters().Length == 1 && typeof(Container).IsAssignableFrom(m.GetParameters()[0].ParameterType)); _getCategory = _categoryServiceType?.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetCategory" && m.IsStatic && m.GetParameters().Length == 1 && typeof(ItemData).IsAssignableFrom(m.GetParameters()[0].ParameterType)); if (_configType != null) { _hasLinks = _configType.GetProperty("HasLinks", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _links = _configType.GetProperty("Links", BindingFlags.Instance | BindingFlags.S