using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("OdinsReach")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+ce1fe04c1e0d3a2923c660dc5676ceccf87de5a5")] [assembly: AssemblyProduct("OdinsReach")] [assembly: AssemblyTitle("OdinsReach")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace OdinsMissingPatch { internal static class ChestFavorites { [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] [Serves(new Type[] { typeof(QuickStack), typeof(ChestButtons) }, Optional = true)] private static class ShowMarks { private static readonly FieldRef Amount = AccessTools.FieldRefAccess("m_amount"); private static Container shownChest; private static uint shownRevision; private static InventoryElement shownFirst; private static bool tinted; private static Color plain; private static bool plainKnown; private static void Prepare() { PanelButtons.Add(ClearButton.Button, PanelButtons.Spot.StackAll, ClearButton.Shows, ClearButton.Refresh); } private static void Postfix(InventoryGui __instance) { //IL_0174: Unknown result type (might be due to invalid IL or missing references) if (PanelButtons.Closing(__instance)) { return; } InventoryGrid containerGrid = __instance.m_containerGrid; if ((Object)(object)containerGrid == (Object)null || !KnowPlain(containerGrid)) { return; } List elements = containerGrid.m_elements; Container currentContainer = __instance.m_currentContainer; ZNetView val = (((Object)(object)currentContainer != (Object)null) ? currentContainer.m_nview : null); if (!Used || (Object)(object)val == (Object)null || !val.IsValid() || containerGrid.m_inventory != currentContainer.GetInventory()) { if (tinted) { Reset(elements); } shownChest = null; return; } uint dataRevision = val.GetZDO().DataRevision; InventoryElement val2 = ((elements.Count > 0) ? elements[0] : null); if ((Object)(object)currentContainer == (Object)(object)shownChest && dataRevision == shownRevision && (Object)(object)val2 == (Object)(object)shownFirst) { return; } shownChest = currentContainer; shownRevision = dataRevision; shownFirst = val2; Reset(elements); string text = Marks(currentContainer); if (text.Length == 0) { return; } Inventory inventory = containerGrid.m_inventory; int width = inventory.GetWidth(); foreach (ItemData allItem in inventory.GetAllItems()) { if (Marked(text, allItem.m_shared.m_name)) { InventoryElement element = containerGrid.GetElement(allItem.m_gridPos.x, allItem.m_gridPos.y, width); Graphic val3 = (((Object)(object)element != (Object)null) ? Amount.Invoke(element) : null); if ((Object)(object)val3 != (Object)null) { val3.color = Palette.ChestYellow; tinted = true; } } } } private static bool KnowPlain(InventoryGrid grid) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (plainKnown) { return true; } InventoryElement val = (((Object)(object)grid.m_elementPrefab != (Object)null) ? grid.m_elementPrefab.GetComponent() : null); Graphic val2 = (((Object)(object)val != (Object)null) ? Amount.Invoke(val) : null); if ((Object)(object)val2 == (Object)null) { return false; } plain = val2.color; plainKnown = true; return true; } private static void Reset(List elements) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) foreach (InventoryElement element in elements) { Graphic val = (((Object)(object)element != (Object)null) ? Amount.Invoke(element) : null); if ((Object)(object)val != (Object)null) { val.color = plain; } } tinted = false; } } private static class ClearButton { internal static readonly PanelButtons.TextButton Button = new PanelButtons.TextButton("OMP_ClearFavorites", new UnityAction(ClearOpen)); private static string favorites = ""; private static int revision = -1; private static int count; private static string tooltip = ""; internal static bool Shows(InventoryGui gui) { Container currentContainer = gui.m_currentContainer; if ((Object)(object)currentContainer != (Object)null && (Object)(object)gui.m_container != (Object)null && ((Component)gui.m_container).gameObject.activeSelf && Used) { return Marks(currentContainer).Length > 0; } return false; } internal static void Refresh(InventoryGui gui) { Describe(gui.m_currentContainer, Marks(gui.m_currentContainer)); Button.SetLabel((count == 1) ? "$omp_clear_favourite" : "$omp_clear_favourites", count.ToString()); Button.SetTooltip("$omp_favourites_topic", tooltip); } private static void Describe(Container chest, string marks) { if (!(marks == favorites) || revision != Translations.Revision) { favorites = marks; revision = Translations.Revision; count = Names(chest).Count; tooltip = "$omp_favourites_tip_head\n" + ChestFavorites.Describe(chest) + "\n\n$omp_favourites_tip_foot"; } } private static void ClearOpen() { InventoryGui instance = InventoryGui.instance; Container val = (((Object)(object)instance != (Object)null) ? instance.m_currentContainer : null); if (!((Object)(object)val == (Object)null)) { Clear(val); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, "$omp_favourites_cleared", 0, (Sprite)null, false); } } } } private static readonly int FavoritesHash = StringExtensionMethods.GetStableHashCode("OMP_ChestFavorites"); private const char Separator = '\n'; internal static bool Used => Patcher.AnyServedOn(typeof(ShowMarks)); internal static string Marks(Container chest) { ZNetView val = (((Object)(object)chest != (Object)null) ? chest.m_nview : null); if (!((Object)(object)val != (Object)null) || !val.IsValid()) { return ""; } return val.GetZDO().GetString(FavoritesHash, ""); } internal static bool Marked(string marks, string itemName) { if (string.IsNullOrEmpty(marks) || string.IsNullOrEmpty(itemName)) { return false; } for (int num = marks.IndexOf(itemName, StringComparison.Ordinal); num >= 0; num = marks.IndexOf(itemName, num + 1, StringComparison.Ordinal)) { int num2 = num + itemName.Length; if (num > 0 && num2 < marks.Length && marks[num - 1] == '\n' && marks[num2] == '\n') { return true; } } return false; } internal static bool Accepts(Container chest, string itemName) { return Marked(Marks(chest), itemName); } internal static List Names(Container chest) { List list = new List(); string[] array = Marks(chest).Split(new char[1] { '\n' }); foreach (string text in array) { if (text.Length > 0) { list.Add(text); } } return list; } internal static bool Toggle(Container chest, string itemName) { List list = Names(chest); bool num = !list.Remove(itemName); if (num) { list.Add(itemName); } Write(chest, list); return num; } internal static void Clear(Container chest) { Write(chest, new List()); } internal static string Describe(Container chest) { StringBuilder stringBuilder = new StringBuilder(); foreach (string item in Names(chest)) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append("- ").Append(Localize(item)); } return stringBuilder.ToString(); } internal static string Localize(string itemName) { if (Localization.instance == null) { return itemName; } return Localization.instance.Localize(itemName); } internal static Container OpenChest(Inventory inventory) { InventoryGui instance = InventoryGui.instance; Container val = (((Object)(object)instance != (Object)null) ? instance.m_currentContainer : null); if (!((Object)(object)val != (Object)null) || inventory == null || val.m_inventory != inventory) { return null; } return val; } private static void Write(Container chest, List names) { ZNetView val = (((Object)(object)chest != (Object)null) ? chest.m_nview : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { return; } if (!val.IsOwner()) { val.ClaimOwnership(); } if (!val.IsOwner()) { return; } StringBuilder stringBuilder = new StringBuilder(); foreach (string name in names) { stringBuilder.Append('\n').Append(name); } if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } val.GetZDO().Set(FavoritesHash, stringBuilder.ToString()); } } internal sealed class ChestGlow : MonoBehaviour { private const float Duration = 7f; private float time; internal static void Flash(Container chest, string text) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)chest == (Object)null)) { GameObject val = (((Object)(object)chest.m_rootObjectOverride != (Object)null) ? ((Component)chest.m_rootObjectOverride).gameObject : ((Component)chest).gameObject); ChestGlow chestGlow = val.GetComponent(); if ((Object)(object)chestGlow == (Object)null) { chestGlow = val.AddComponent(); } chestGlow.time = 0f; if (text != null) { ShowText(((Component)chest).transform.position + Vector3.up, text); } } } private static void ShowText(Vector3 position, string text) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) DamageText instance = DamageText.instance; if (!((Object)(object)instance == (Object)null)) { Camera mainCamera = Utils.GetMainCamera(); float num = (((Object)(object)mainCamera != (Object)null) ? Vector3.Distance(((Component)mainCamera).transform.position, position) : 0f); instance.AddInworldText((TextType)7, position, num, text, false); } } private void Update() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) time += Time.deltaTime; MaterialMan instance = MaterialMan.instance; if ((Object)(object)instance == (Object)null || time >= 7f) { Object.Destroy((Object)(object)this); return; } float num = Mathf.Abs(Mathf.Sin(time / 7f * (float)Math.PI * 3f)); instance.SetValue(((Component)this).gameObject, ShaderProps._EmissionColor, Palette.Gold * (0.6f * num), false); instance.SetValue(((Component)this).gameObject, ShaderProps._Color, Color.Lerp(Color.white, Palette.Gold, 0.6f * num), false); } private void OnDestroy() { MaterialMan instance = MaterialMan.instance; if ((Object)(object)instance != (Object)null) { instance.ResetValue(((Component)this).gameObject, ShaderProps._EmissionColor); instance.ResetValue(((Component)this).gameObject, ShaderProps._Color); } } } internal static class InventorySorter { internal static bool Sort(Inventory inventory, int fromRow, Func keep) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); int width = inventory.GetWidth(); int height = inventory.GetHeight(); bool[,] array = new bool[width, height]; List list = new List(); foreach (ItemData item in allItems) { Vector2i gridPos = item.m_gridPos; bool flag = gridPos.x >= 0 && gridPos.x < width && gridPos.y >= 0 && gridPos.y < height; if (flag && gridPos.y >= fromRow && (keep == null || !keep(item))) { list.Add(item); } else if (flag) { array[gridPos.x, gridPos.y] = true; } } if (list.Count == 0) { return false; } List list2 = new List(); for (int i = fromRow; i < height; i++) { for (int j = 0; j < width; j++) { if (!array[j, i]) { list2.Add(new Vector2i(j, i)); } } } if (list2.Count < list.Count) { return false; } MaterialOrder.Prepare(); list.Sort(Compare); List list3 = Merge(list, allItems); for (int k = 0; k < list3.Count; k++) { list3[k].m_gridPos = list2[k]; } inventory.Changed(false, false); return true; } private static List Merge(List sorted, List all) { List list = new List(sorted.Count); foreach (ItemData item in sorted) { if (item.m_shared.m_maxStackSize > 1) { foreach (ItemData item2 in list) { if (item.m_stack <= 0) { break; } if (Merges(item2, item)) { int num = Math.Min(item2.m_shared.m_maxStackSize - item2.m_stack, item.m_stack); item2.m_stack += num; item.m_stack -= num; } } } if (item.m_stack > 0) { list.Add(item); } else { all.Remove(item); } } return list; } internal static bool Merges(ItemData stack, ItemData item) { if (stack.m_stack < stack.m_shared.m_maxStackSize) { return SameStack(stack, item); } return false; } private static bool SameStack(ItemData a, ItemData b) { if (a.m_shared.m_name != b.m_shared.m_name || a.m_quality != b.m_quality || a.m_variant != b.m_variant || a.m_worldLevel != b.m_worldLevel || a.m_cheated != b.m_cheated) { return false; } if (Covers(a.m_customData, b.m_customData)) { return Covers(b.m_customData, a.m_customData); } return false; } private static bool Covers(Dictionary a, Dictionary b) { foreach (KeyValuePair item in a) { if (item.Key != "OMP_Favorite" && (!b.TryGetValue(item.Key, out var value) || value != item.Value)) { return false; } } return true; } private static int Compare(ItemData a, ItemData b) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 int num = Rank(a.m_shared.m_itemType).CompareTo(Rank(b.m_shared.m_itemType)); if (num != 0) { return num; } if ((int)a.m_shared.m_itemType == 1 && (int)b.m_shared.m_itemType == 1) { num = Portable(a).CompareTo(Portable(b)); if (num != 0) { return num; } num = MaterialOrder.Compare(a, b); if (num != 0) { return num; } } num = string.Compare(DisplayName(a), DisplayName(b), StringComparison.CurrentCultureIgnoreCase); if (num != 0) { return num; } num = b.m_quality.CompareTo(a.m_quality); if (num != 0) { return num; } num = a.m_variant.CompareTo(b.m_variant); if (num == 0) { return b.m_stack.CompareTo(a.m_stack); } return num; } private static int Portable(ItemData item) { return (!item.m_shared.m_teleportable) ? 1 : 0; } private static string DisplayName(ItemData item) { string name = item.m_shared.m_name; Localization instance = Localization.instance; if (instance == null) { return name; } return instance.Localize(name); } private static int Rank(ItemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected I4, but got Unknown switch (type - 1) { case 2: case 3: case 13: case 14: case 19: case 21: return 0; case 4: return 1; case 18: return 2; case 5: return 3; case 6: return 4; case 10: return 5; case 16: return 6; case 11: return 7; case 9: return 8; case 17: return 9; case 23: return 10; case 8: return 11; case 22: return 12; case 1: return 13; case 20: return 14; case 0: return 15; case 12: return 16; case 15: return 17; default: return 18; } } } internal static class MaterialOrder { private struct Place { internal int Family; internal int Depth; } private sealed class Step { internal SharedData Result; internal readonly List Inputs = new List(); internal int Station = -1; internal int Level; } private sealed class Group { internal readonly List Members = new List(); internal int Low = int.MaxValue; internal string Name; internal void Note(SharedData kind, int depth) { Members.Add(kind); string text = Label(kind); if (depth < Low || (depth == Low && string.Compare(text, Name, StringComparison.CurrentCultureIgnoreCase) < 0)) { Low = depth; Name = text; } } } private const int MaxPasses = 32; private static ObjectDB source; private static int sourceRecipes; private static int sourceItems; private static Dictionary places; internal static void Prepare() { ObjectDB instance = ObjectDB.instance; if (!((Object)(object)instance == (Object)null) && (!((Object)(object)instance == (Object)(object)source) || instance.m_recipes.Count != sourceRecipes || instance.m_items.Count != sourceItems)) { source = instance; sourceRecipes = instance.m_recipes.Count; sourceItems = instance.m_items.Count; places = Build(instance); } } internal static int Compare(ItemData a, ItemData b) { Place place = Of(a); Place place2 = Of(b); int num = place.Family.CompareTo(place2.Family); if (num == 0) { return place.Depth.CompareTo(place2.Depth); } return num; } private static Place Of(ItemData item) { if (places != null && item != null && item.m_shared != null && places.TryGetValue(item.m_shared, out var value)) { return value; } return new Place { Family = int.MaxValue, Depth = 0 }; } private static Dictionary Build(ObjectDB db) { List allBuildPieces = db.GetAllBuildPieces(true); Dictionary stations = Stations(allBuildPieces); List steps = Steps(db, allBuildPieces, stations); int[] pieceDepth = new int[allBuildPieces.Count]; Dictionary made = new Dictionary(); Settle(allBuildPieces, stations, Producers(steps), pieceDepth, made); Dictionary dictionary = Depths(made, Uses(steps, allBuildPieces, made, pieceDepth)); Dictionary parents = Families(steps); Dictionary dictionary2 = new Dictionary(); foreach (KeyValuePair item in dictionary) { SharedData key = Find(parents, item.Key); if (!dictionary2.TryGetValue(key, out var value)) { value = (dictionary2[key] = new Group()); } value.Note(item.Key, item.Value); } List list = new List(dictionary2.Values); list.Sort(CompareGroups); Dictionary dictionary3 = new Dictionary(); for (int i = 0; i < list.Count; i++) { foreach (SharedData member in list[i].Members) { dictionary3[member] = new Place { Family = i, Depth = dictionary[member] }; } } return dictionary3; } private static Dictionary Stations(List pieces) { Dictionary dictionary = new Dictionary(); for (int i = 0; i < pieces.Count; i++) { Piece val = pieces[i]; if (!((Object)(object)val == (Object)null)) { CraftingStation component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && !dictionary.ContainsKey(component)) { dictionary[component] = i; } } } return dictionary; } private static List Steps(ObjectDB db, List pieces, Dictionary stations) { List list = new List(); foreach (Recipe recipe in db.m_recipes) { if (!((Object)(object)recipe == (Object)null) && recipe.m_enabled && !((Object)(object)recipe.m_item == (Object)null)) { Step step = new Step { Result = recipe.m_item.m_itemData.m_shared, Station = StationPiece(stations, recipe.m_craftingStation), Level = Math.Max(0, recipe.m_minStationLevel - 1) }; Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { Feed(step.Inputs, val?.m_resItem); } Keep(list, step); } } for (int j = 0; j < pieces.Count; j++) { Piece val2 = pieces[j]; if ((Object)(object)val2 == (Object)null) { continue; } Smelter component = ((Component)val2).GetComponent(); if ((Object)(object)component != (Object)null) { foreach (ItemConversion item in component.m_conversion) { if (item != null) { Converted(list, j, item.m_from, item.m_to); } } } CookingStation component2 = ((Component)val2).GetComponent(); if ((Object)(object)component2 != (Object)null) { foreach (ItemConversion item2 in component2.m_conversion) { if (item2 != null) { Converted(list, j, item2.m_from, item2.m_to); } } } Fermenter component3 = ((Component)val2).GetComponent(); if (!((Object)(object)component3 != (Object)null)) { continue; } foreach (ItemConversion item3 in component3.m_conversion) { if (item3 != null) { Converted(list, j, item3.m_from, item3.m_to); } } } return list; } private static void Converted(List steps, int station, ItemDrop from, ItemDrop to) { if (!((Object)(object)to == (Object)null)) { Step step = new Step { Result = to.m_itemData.m_shared, Station = station }; Feed(step.Inputs, from); Keep(steps, step); } } private static void Feed(List inputs, ItemDrop drop) { SharedData val = (((Object)(object)drop == (Object)null) ? null : drop.m_itemData.m_shared); if (val != null && !inputs.Contains(val)) { inputs.Add(val); } } private static void Keep(List steps, Step step) { if (step.Result != null) { steps.Add(step); } } private static Dictionary> Producers(List steps) { Dictionary> dictionary = new Dictionary>(); foreach (Step step in steps) { if (!dictionary.TryGetValue(step.Result, out var value)) { value = new List(); dictionary[step.Result] = value; } value.Add(step); } return dictionary; } private static void Settle(List pieces, Dictionary stations, Dictionary> producers, int[] pieceDepth, Dictionary made) { bool flag = true; for (int i = 0; i < 32 && flag; i++) { flag = false; for (int j = 0; j < pieces.Count; j++) { Piece val = pieces[j]; if ((Object)(object)val == (Object)null) { continue; } int num = StationDepth(stations, pieceDepth, val.m_craftingStation); Requirement[] resources = val.m_resources; foreach (Requirement val2 in resources) { if (val2 != null && (Object)(object)val2.m_resItem != (Object)null) { num = Math.Max(num, Depth(made, val2.m_resItem.m_itemData.m_shared)); } } if (num + 1 > pieceDepth[j]) { pieceDepth[j] = num + 1; flag = true; } } foreach (KeyValuePair> producer in producers) { int num2 = int.MaxValue; foreach (Step item in producer.Value) { num2 = Math.Min(num2, StepDepth(item, made, pieceDepth)); } if (num2 != int.MaxValue && num2 > Depth(made, producer.Key)) { made[producer.Key] = num2; flag = true; } } } } private static Dictionary Uses(List steps, List pieces, Dictionary made, int[] pieceDepth) { Dictionary dictionary = new Dictionary(); foreach (Step step in steps) { int depth = StepDepth(step, made, pieceDepth); foreach (SharedData input in step.Inputs) { Lower(dictionary, input, depth); } } for (int i = 0; i < pieces.Count; i++) { Piece val = pieces[i]; if ((Object)(object)val == (Object)null) { continue; } Requirement[] resources = val.m_resources; foreach (Requirement val2 in resources) { if (val2 != null && (Object)(object)val2.m_resItem != (Object)null) { Lower(dictionary, val2.m_resItem.m_itemData.m_shared, pieceDepth[i]); } } } return dictionary; } private static Dictionary Depths(Dictionary made, Dictionary used) { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair item in used) { if (Material(item.Key)) { dictionary[item.Key] = item.Value; } } foreach (KeyValuePair item2 in made) { if (Material(item2.Key) && !dictionary.ContainsKey(item2.Key)) { dictionary[item2.Key] = item2.Value; } } return dictionary; } private static Dictionary Families(List steps) { Dictionary dictionary = new Dictionary(); foreach (Step step in steps) { if (!Material(step.Result)) { continue; } foreach (SharedData input in step.Inputs) { if (Material(input)) { Union(dictionary, step.Result, input); } } } return dictionary; } private static SharedData Find(Dictionary parents, SharedData kind) { if (!parents.TryGetValue(kind, out var value)) { parents[kind] = kind; return kind; } if (value == kind) { return kind; } return parents[kind] = Find(parents, value); } private static void Union(Dictionary parents, SharedData a, SharedData b) { SharedData val = Find(parents, a); SharedData val2 = Find(parents, b); if (val != val2) { parents[val2] = val; } } private static int CompareGroups(Group a, Group b) { int num = a.Low.CompareTo(b.Low); if (num == 0) { return string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase); } return num; } private static int StepDepth(Step step, Dictionary made, int[] pieceDepth) { int num = ((step.Station >= 0) ? (pieceDepth[step.Station] + step.Level) : step.Level); foreach (SharedData input in step.Inputs) { num = Math.Max(num, Depth(made, input)); } return num + 1; } private static int StationPiece(Dictionary stations, CraftingStation station) { if (!((Object)(object)station != (Object)null) || !stations.TryGetValue(station, out var value)) { return -1; } return value; } private static int StationDepth(Dictionary stations, int[] pieceDepth, CraftingStation station) { int num = StationPiece(stations, station); if (num < 0) { return 0; } return pieceDepth[num]; } private static int Depth(Dictionary depths, SharedData kind) { if (kind == null || !depths.TryGetValue(kind, out var value)) { return 0; } return value; } private static void Lower(Dictionary depths, SharedData kind, int depth) { if (kind != null && (!depths.TryGetValue(kind, out var value) || depth < value)) { depths[kind] = depth; } } private static bool Material(SharedData kind) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (kind != null) { return (int)kind.m_itemType == 1; } return false; } private static string Label(SharedData kind) { Localization instance = Localization.instance; if (instance == null) { return kind.m_name; } return instance.Localize(kind.m_name); } } internal static class NearbyChests { private sealed class Entry { public Container Container; public Piece Piece; public bool Tombstone; } internal readonly struct Scope : IDisposable { private readonly bool entered; internal Scope(Tweak tweak, Humanoid user) { entered = EnterReach(tweak, user); } public void Dispose() { LeaveReach(entered); } } [HarmonyPatch(typeof(Inventory), "CountItems")] [Serves(new Type[] { typeof(NearbyCrafting), typeof(StationRefill) })] private static class CountChests { private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { if (InReach && name != null && IsBackpack(__instance)) { __result += InChests(name, quality, matchWorldLevel); } } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] [Serves(new Type[] { typeof(NearbyCrafting), typeof(StationRefill) })] private static class HaveInChests { private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { if (!__result && InReach && name != null && IsBackpack(__instance)) { __result = InChests(name, -1, matchWorldLevel) > 0; } } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] [Serves(new Type[] { typeof(NearbyCrafting), typeof(StationRefill) })] private static class TakeFromChests { private static void Prefix(Inventory __instance, string name, ref int amount, int itemQuality, bool worldLevelBased) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (!InReach || name == null || !IsBackpack(__instance)) { return; } int num = Carried(__instance, name, itemQuality, worldLevelBased); if (amount <= num) { return; } int num2 = amount - num; amount = num; foreach (Container item in new List(Find(((Component)Player.m_localPlayer).transform.position, ReachRange))) { if (num2 <= 0) { break; } Inventory inventory = item.GetInventory(); if (inventory.CountItems(name, itemQuality, worldLevelBased) > 0 && Claim(item)) { int num3 = Mathf.Min(num2, inventory.CountItems(name, itemQuality, worldLevelBased)); if (num3 > 0) { inventory.RemoveItem(name, num3, itemQuality, worldLevelBased); num2 -= num3; } } } counts.Clear(); } } private readonly struct CountKey : IEquatable { private readonly string name; private readonly int quality; private readonly bool matchWorldLevel; public CountKey(string name, int quality, bool matchWorldLevel) { this.name = name; this.quality = quality; this.matchWorldLevel = matchWorldLevel; } public bool Equals(CountKey other) { if (quality == other.quality && matchWorldLevel == other.matchWorldLevel) { return string.Equals(name, other.name, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { if (obj is CountKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (((name.GetHashCode() * 397) ^ quality) * 397) ^ (matchWorldLevel ? 1 : 0); } } [HarmonyPatch(typeof(Container), "Awake")] [Serves(new Type[] { typeof(QuickStack), typeof(StationRefill), typeof(NearbyCrafting) })] [LoadHook] private static class Register { private static void Postfix(Container __instance) { if (__instance.m_inventory != null) { Registry.Add(new Entry { Container = __instance, Piece = ((Component)__instance).GetComponentInParent(), Tombstone = ((Object)(object)((Component)__instance).GetComponent() != (Object)null) }); } } } [HarmonyPatch(typeof(Container), "GetHoverText")] [Serves(new Type[] { typeof(QuickStack), typeof(StationRefill), typeof(NearbyCrafting) }, Optional = true)] private static class HoverText { private static void Prepare() { PanelButtons.Add(NearbyUse, PanelButtons.Spot.TakeAll, ShowsNearbyUse, RefreshNearbyUse); } private static void Postfix(Container __instance, ref string __result) { if (AnyTweakOn && IsExcluded(__instance)) { __result = __result + "\n" + Localization.instance.Localize("$omp_nearby_off") + ""; } } } private static readonly int ExcludedHash = StringExtensionMethods.GetStableHashCode("OMP_NoNearbyUse"); private static readonly List Registry = new List(); private static readonly List Found = new List(); private static int reachDepth; private static readonly Dictionary counts = new Dictionary(); private static int countsFrame = -1; private static readonly PanelButtons.TextButton NearbyUse = new PanelButtons.TextButton("OMP_NearbyUse", new UnityAction(Toggle)); internal static bool AnyTweakOn => Patcher.AnyServedOn(typeof(Register)); private static bool InReach => reachDepth > 0; private static float ReachRange => SharedSettings.ChestRange.Value; internal static List Find(Vector3 origin) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Find(origin, SharedSettings.ChestRange.Value); } internal static List Find(Vector3 origin, float radius) { //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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) Found.Clear(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return Found; } long playerID = localPlayer.GetPlayerID(); float num = radius * radius; for (int num2 = Registry.Count - 1; num2 >= 0; num2--) { Entry entry = Registry[num2]; if ((Object)(object)entry.Container == (Object)null) { Registry.RemoveAt(num2); } else { Vector3 val = ((Component)entry.Container).transform.position - origin; if (!(((Vector3)(ref val)).sqrMagnitude > num) && IsInReach(entry, playerID)) { Found.Add(entry.Container); } } } Found.Sort(delegate(Container a, Container b) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) Vector3 val2 = ((Component)a).transform.position - origin; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; val2 = ((Component)b).transform.position - origin; return sqrMagnitude.CompareTo(((Vector3)(ref val2)).sqrMagnitude); }); return Found; } private static bool IsInReach(Entry entry, long playerId) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Invalid comparison between Unknown and I4 //IL_00d5: Unknown result type (might be due to invalid IL or missing references) Container container = entry.Container; if (!Eligible(entry) || container.m_inventory == null) { return false; } ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } if (nview.GetZDO().GetBool(ExcludedHash, false)) { return false; } InventoryGui instance = InventoryGui.instance; if (!((Object)(object)instance != (Object)null) || !((Object)(object)instance.m_currentContainer == (Object)(object)container)) { if (container.IsInUse() || nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1) { return false; } if ((Object)(object)container.m_wagon != (Object)null && container.m_wagon.InUse()) { return false; } } if ((int)container.m_privacy != 2 && (Object)(object)container.m_piece == (Object)null) { return false; } if (!container.CheckAccess(playerId)) { return false; } if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false)) { return false; } return true; } internal static bool Claim(Container chest) { if ((Object)(object)chest == (Object)null) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } Entry entry = Registry.Find((Entry e) => (Object)(object)e.Container == (Object)(object)chest); if (entry == null || !IsInReach(entry, localPlayer.GetPlayerID())) { return false; } ZNetView nview = chest.m_nview; chest.Load(); if (!nview.IsOwner()) { nview.ClaimOwnership(); } return nview.IsOwner(); } private static bool Eligible(Container chest) { return Eligible(Registry.Find((Entry e) => (Object)(object)e.Container == (Object)(object)chest)); } private static bool Eligible(Entry entry) { if (entry != null && !entry.Tombstone && (Object)(object)entry.Piece != (Object)null) { return entry.Piece.IsPlacedByPlayer(); } return false; } internal static bool IsExcluded(Container chest) { ZNetView val = (((Object)(object)chest != (Object)null) ? chest.m_nview : null); if ((Object)(object)val != (Object)null && val.IsValid()) { return val.GetZDO().GetBool(ExcludedHash, false); } return false; } private static void SetExcluded(Container chest, bool excluded) { ZNetView val = (((Object)(object)chest != (Object)null) ? chest.m_nview : null); if (!((Object)(object)val == (Object)null) && val.IsValid()) { if (!val.IsOwner()) { val.ClaimOwnership(); } if (val.IsOwner()) { val.GetZDO().Set(ExcludedHash, excluded); } } } internal static bool EnterReach(Tweak tweak, Humanoid user) { if (!tweak.On || (Object)(object)user == (Object)null || (Object)(object)user != (Object)(object)Player.m_localPlayer) { return false; } reachDepth++; return true; } internal static void LeaveReach(bool entered) { if (entered && reachDepth > 0) { reachDepth--; } } private static bool IsBackpack(Inventory inventory) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { return inventory == ((Humanoid)localPlayer).GetInventory(); } return false; } internal static int Carried(Inventory inventory, string name, int quality, bool matchWorldLevel) { int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.m_shared.m_name == name && (quality < 0 || allItem.m_quality == quality) && (!matchWorldLevel || allItem.m_worldLevel >= Game.m_worldLevel)) { num += allItem.m_stack; } } return num; } private static int InChests(string name, int quality, bool matchWorldLevel) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (Time.frameCount != countsFrame) { counts.Clear(); countsFrame = Time.frameCount; } CountKey key = new CountKey(name, quality, matchWorldLevel); if (counts.TryGetValue(key, out var value)) { return value; } foreach (Container item in Find(((Component)Player.m_localPlayer).transform.position, ReachRange)) { value += item.GetInventory().CountItems(name, quality, matchWorldLevel); } counts[key] = value; return value; } private static bool ShowsNearbyUse(InventoryGui gui) { Container currentContainer = gui.m_currentContainer; if ((Object)(object)currentContainer != (Object)null && (Object)(object)gui.m_container != (Object)null && ((Component)gui.m_container).gameObject.activeSelf && AnyTweakOn) { return Eligible(currentContainer); } return false; } private static void RefreshNearbyUse(InventoryGui gui) { NearbyUse.SetLabel(IsExcluded(gui.m_currentContainer) ? "$omp_nearby_off" : "$omp_nearby_on"); NearbyUse.SetTooltip("$omp_nearby_topic", "$omp_nearby_tip"); } private static void Toggle() { InventoryGui instance = InventoryGui.instance; Container val = (((Object)(object)instance != (Object)null) ? instance.m_currentContainer : null); if ((Object)(object)val != (Object)null) { SetExcluded(val, !IsExcluded(val)); } } } [BepInPlugin("rauschekuh.odinsreach", "Odin's Reach", "0.1.0")] public class OdinsReachPlugin : BaseUnityPlugin { public const string GUID = "rauschekuh.odinsreach"; public const string NAME = "Odin's Reach"; public const string VERSION = "0.1.0"; private static readonly Tweak[] Tweaks = new Tweak[5] { NearbyCrafting.Instance, QuickStack.Instance, StationRefill.Instance, ChestButtons.Instance, InventoryButtons.Instance }; private void Awake() { TweakHost.Start((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config, "rauschekuh.odinsreach", Tweaks, SharedSettings.Bind); } } internal static class PanelButtons { internal enum Column { Inventory, Chest } internal enum Spot { TakeAll, StackAll } private sealed class IconEntry { internal Column Column; internal int Order; internal string Name; internal string Icon; internal string Title; internal Func Tooltip; internal UnityAction OnClick; internal Func Visible; internal Button Button; internal UITooltip Tip; } private sealed class TextEntry { internal TextButton Button; internal Spot Spot; internal Func Visible; internal Action Refresh; } [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] [Serves(new Type[] { typeof(ChestButtons), typeof(InventoryButtons) })] private static class LayOut { private static void Postfix(InventoryGui __instance) { Arrange(__instance); } } [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] [Serves(new Type[] { typeof(QuickStack), typeof(StationRefill), typeof(NearbyCrafting) }, Optional = true)] private static class LayOutText { private static void Postfix(InventoryGui __instance) { Arrange(__instance); } } [HarmonyPatch(typeof(InventoryGui), "OnDestroy")] [Serves(new Type[] { typeof(ChestButtons), typeof(InventoryButtons) })] private static class ScreenDestroyed { private static void Postfix() { foreach (IconEntry iconButton in iconButtons) { iconButton.Button = null; } foreach (TextEntry textButton in textButtons) { textButton.Button.Forget(); } vanillaHidden = false; } } internal sealed class TextButton { private const float Margin = 16f; private readonly string name; private readonly UnityAction onClick; private Button button; private UITooltip tip; private string label; private string tooltip; private Component labelText; private PropertyInfo labelWidth; internal TextButton(string name, UnityAction onClick) { this.name = name; this.onClick = onClick; } internal bool Show(InventoryGui gui) { if ((Object)(object)button == (Object)null && !Create(gui)) { return false; } ((Component)button).gameObject.SetActive(true); return true; } internal void Hide() { if ((Object)(object)button != (Object)null) { ((Component)button).gameObject.SetActive(false); } } internal void Forget() { button = null; } internal void Place(InventoryGui gui, Button at, bool keepLeft, int slot) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) RectTransform val = ((!((Object)(object)gui.m_takeAllButton != (Object)null)) ? ((RectTransform)null) : ((RectTransform)((Component)gui.m_takeAllButton).transform)); if (!((Object)(object)val == (Object)null)) { Rect rect = val.rect; float height = ((Rect)(ref rect)).height; rect = val.rect; float num = Mathf.Max(((Rect)(ref rect)).width, LabelWidth() + 32f); RectTransform val2 = (RectTransform)((Component)button).transform; val2.sizeDelta = new Vector2(num, height); RectTransform val3 = ((!((Object)(object)at != (Object)null)) ? ((RectTransform)null) : ((RectTransform)((Component)at).transform)); if ((Object)(object)val3 == (Object)null) { RectTransform container = gui.m_container; Pin(val2, new Vector2(ColumnLeft(container) + num * 0.5f, ColumnTop(container) - height * 0.5f - (float)slot * (height + 6f))); return; } val2.anchorMin = val3.anchorMin; val2.anchorMax = val3.anchorMax; val2.pivot = val3.pivot; rect = val3.rect; float num2 = num - ((Rect)(ref rect)).width; val2.anchoredPosition = val3.anchoredPosition + new Vector2(keepLeft ? (num2 * (1f - val3.pivot.x)) : ((0f - num2) * val3.pivot.x), 0f); } } private float LabelWidth() { if ((Object)(object)labelText == (Object)null) { Transform val = ((Component)button).transform.Find("Text"); if ((Object)(object)val == (Object)null) { return 0f; } Component[] components = ((Component)val).GetComponents(); foreach (Component val2 in components) { PropertyInfo property = ((object)val2).GetType().GetProperty("preferredWidth", typeof(float)); if (property != null && property.CanRead) { labelText = val2; labelWidth = property; break; } } } if (!((Object)(object)labelText != (Object)null)) { return 0f; } return (float)labelWidth.GetValue(labelText, null); } private bool Create(InventoryGui gui) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown Button takeAllButton = gui.m_takeAllButton; if ((Object)(object)takeAllButton == (Object)null) { return false; } GameObject val = Object.Instantiate(((Component)takeAllButton).gameObject, ((Component)takeAllButton).transform.parent); ((Object)val).name = name; button = val.GetComponent