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 BalancedFisherman v0.1.0
plugins/BalancedFisherman/BalancedFisherman.dll
Decompiled 2 days agousing 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 BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using ConditionalConfigSync; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("BalancedFisherman.Probe")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("BalancedFisherman")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("BalancedFisherman")] [assembly: AssemblyTitle("BalancedFisherman")] [assembly: AssemblyVersion("0.1.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 Vercadi.BalancedFisherman { internal sealed class CatchRegistry { internal readonly Dictionary<string, GameObject> Items = new Dictionary<string, GameObject>(StringComparer.Ordinal); internal readonly List<Catch> Options = new List<Catch>(); internal Dictionary<string, long> Weights; private readonly Dictionary<string, string> sharedNames = new Dictionary<string, string>(StringComparer.Ordinal); internal string Name(ItemData item) { if (Object.op_Implicit((Object)(object)item.m_dropPrefab)) { return ((Object)item.m_dropPrefab).name; } if (sharedNames.TryGetValue(item.m_shared.m_name, out var value)) { return value; } throw new InvalidOperationException("Unresolved inventory item in fish stock snapshot: " + item.m_shared.m_name); } internal static CatchRegistry Build() { //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Invalid comparison between Unknown and I4 //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Invalid comparison between Unknown and I4 CatchRegistry catchRegistry = new CatchRegistry(); foreach (GameObject item in ObjectDB.instance.m_items) { if (!Object.op_Implicit((Object)(object)item)) { continue; } ItemDrop component = item.GetComponent<ItemDrop>(); if (Object.op_Implicit((Object)(object)component)) { catchRegistry.Items[((Object)item).name] = item; if (!catchRegistry.sharedNames.ContainsKey(component.m_itemData.m_shared.m_name)) { catchRegistry.sharedNames[component.m_itemData.m_shared.m_name] = ((Object)item).name; } } } Catch[] catalog = Rules.Catalog; foreach (Catch obj in catalog) { if (catchRegistry.Items.TryGetValue(obj.Prefab, out var value) && !Object.op_Implicit((Object)(object)value.GetComponent<Character>())) { catchRegistry.Options.Add(obj); } } foreach (Catch item2 in Rules.Catalog.Where((Catch x) => !x.Prefab.EndsWith("_RtD", StringComparison.Ordinal))) { if (!catchRegistry.Options.Contains(item2)) { throw new InvalidOperationException("Missing vanilla catch item: " + item2.Prefab); } } List<Conversion> list = new List<Conversion>(); foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if (!Object.op_Implicit((Object)(object)recipe) || !recipe.m_enabled || !Object.op_Implicit((Object)(object)recipe.m_item) || recipe.m_amount <= 0 || recipe.m_resources == null) { continue; } SharedData shared = recipe.m_item.m_itemData.m_shared; if ((int)shared.m_itemType != 1 && (int)shared.m_itemType != 2) { continue; } Conversion conversion = new Conversion { Output = ((Object)recipe.m_item).name, Amount = recipe.m_amount, Any = recipe.m_requireOnlyOneIngredient }; foreach (Requirement item3 in recipe.m_resources.Where((Requirement q) => q != null && Object.op_Implicit((Object)(object)q.m_resItem) && q.m_amount > 0)) { string name = ((Object)item3.m_resItem).name; conversion.Inputs[name] = checked((conversion.Inputs.TryGetValue(name, out var value2) ? value2 : 0) + item3.m_amount); } if (conversion.Inputs.Count > 0) { list.Add(conversion); } } List<Conversion> list2 = new List<Conversion>(); foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if (!Object.op_Implicit((Object)(object)prefab)) { continue; } CookingStation[] componentsInChildren = prefab.GetComponentsInChildren<CookingStation>(true); for (int i = 0; i < componentsInChildren.Length; i++) { foreach (ItemConversion item4 in componentsInChildren[i].m_conversion) { if (Object.op_Implicit((Object)(object)item4.m_from) && Object.op_Implicit((Object)(object)item4.m_to)) { list2.Add(One(((Object)item4.m_from).name, ((Object)item4.m_to).name, 1)); } } } Smelter[] componentsInChildren2 = prefab.GetComponentsInChildren<Smelter>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { foreach (ItemConversion item5 in componentsInChildren2[i].m_conversion) { if (Object.op_Implicit((Object)(object)item5.m_from) && Object.op_Implicit((Object)(object)item5.m_to)) { list2.Add(One(((Object)item5.m_from).name, ((Object)item5.m_to).name, 1)); } } } Fermenter[] componentsInChildren3 = prefab.GetComponentsInChildren<Fermenter>(true); for (int i = 0; i < componentsInChildren3.Length; i++) { foreach (ItemConversion item6 in componentsInChildren3[i].m_conversion) { if (Object.op_Implicit((Object)(object)item6.m_from) && Object.op_Implicit((Object)(object)item6.m_to) && item6.m_producedItems > 0) { list2.Add(One(((Object)item6.m_from).name, ((Object)item6.m_to).name, item6.m_producedItems)); } } } } string[] seeds = catchRegistry.Options.Select((Catch o) => o.Prefab).Concat(new string[1] { "FishRaw" }).ToArray(); IEnumerable<string> food = (from p in catchRegistry.Items.Where(delegate(KeyValuePair<string, GameObject> p) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 SharedData shared2 = p.Value.GetComponent<ItemDrop>().m_itemData.m_shared; return (int)shared2.m_itemType == 2 && (shared2.m_food > 0f || shared2.m_foodStamina > 0f || shared2.m_foodEitr > 0f); }) select p.Key).Concat(new string[1] { "FeastOceans_Material" }); HashSet<string> allowed = Rules.FoodChain(seeds, food, list2); catchRegistry.Weights = Rules.FoodWeights(seeds, from c in list.Concat(list2) where allowed.Contains(c.Output) select c); if (!catchRegistry.Weights.ContainsKey("FishCooked")) { throw new InvalidOperationException("Cooked-fish stock mapping is missing."); } return catchRegistry; } private static Conversion One(string from, string to, int amount) { return new Conversion { Output = to, Amount = amount, Inputs = new Dictionary<string, int> { { from, 1 } } }; } internal void Export(string path) { List<string> list = new List<string> { "Kind\tPrefab\tName\tWeight\tBoss requirement\tStock units" }; foreach (Catch option in Options) { string[] obj = new string[10] { "catch\t", option.Prefab, "\t", Localization.instance.Localize(Items[option.Prefab].GetComponent<ItemDrop>().m_itemData.m_shared.m_name), "\t", null, null, null, null, null }; int weight = option.Weight; obj[5] = weight.ToString(); obj[6] = "\t"; obj[7] = option.Key; obj[8] = "\t"; obj[9] = Weights[option.Prefab].ToString(); list.Add(string.Concat(obj)); } foreach (KeyValuePair<string, long> item in Weights.OrderBy((KeyValuePair<string, long> x) => x.Key)) { list.Add("stock\t" + item.Key + "\t\t\t\t" + item.Value); } File.WriteAllLines(path, list); } } internal static class Compatibility { private const BindingFlags Fields = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; internal static bool SupportedVersion(Version version) { if (version != null && version.Major == 0 && version.Minor == 8 && version.Build == 5) { return version.Revision <= 0; } return false; } internal static void RequireApi(Type dock, Type container, Type view, Type gameObject) { Field(dock, "m_targetContainer", container); Field(dock, "m_productionEffect", gameObject); Method(dock, "UpdateFishProduction", typeof(void)); Field(container, "m_nview", view); Method(container, "Load", typeof(bool)); Method(container, "Save", typeof(void)); Method(container, "Awake", typeof(void)); Method(container, "OnDestroyed", typeof(void)); Method(container, "GetHoverText", typeof(string)); } private static void Field(Type type, string name, Type expected) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || field.FieldType != expected) { throw new NotSupportedException("Unsupported fishing API field: " + type.Name + "." + name); } } private static void Method(Type type, string name, Type expected) { MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method == null || method.ReturnType != expected) { throw new NotSupportedException("Unsupported fishing API method: " + type.Name + "." + name); } } } [BepInPlugin("vercadi.BalancedFisherman", "Balanced Fisherman", "0.1.0")] [BepInDependency("marlthon.OdinShipPlus", "0.8.5")] [BepInDependency("_shudnal.ConditionalConfigSync", "1.0.9")] public sealed class Plugin : BaseUnityPlugin { internal sealed class Clock { internal float Last; internal bool Owner; internal bool Active; } private sealed class StockSnapshot { internal byte[] Data; internal CatchRegistry Registry; internal long Count; } public const string Id = "vercadi.BalancedFisherman"; public const string Version = "0.1.0"; internal static Plugin Instance; internal const string ProgressKey = "vercadi_fisher_progress"; internal const string StatusKey = "vercadi_fisher_status"; private Harmony harmony; private ConfigSync sync; internal bool Faulted; private ConfigEntry<bool> productionEnabled; private ConfigEntry<float> interval; private ConfigEntry<float> chance; private ConfigEntry<float> radius; private ConfigEntry<int> cap; internal readonly HashSet<Container> Containers = new HashSet<Container>(); internal readonly ConditionalWeakTable<OdinFishingDock, Clock> Clocks = new ConditionalWeakTable<OdinFishingDock, Clock>(); internal CatchRegistry Registry; private ObjectDB preparedDb; private ZNetScene preparedScene; private float nextRegistryCheck; private float registryReadyAt; private string waiting; private readonly ConditionalWeakTable<Container, StockSnapshot> stockCache = new ConditionalWeakTable<Container, StockSnapshot>(); private void Awake() { //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_001c: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Expected O, but got Unknown //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Expected O, but got Unknown Instance = this; sync = new ConfigSync("vercadi.BalancedFisherman") { DisplayName = "Balanced Fisherman", CurrentVersion = "0.1.0", MinimumRequiredVersion = "0.1.0", ModRequired = true }; ((ConditionalConfigSync)sync).AddLockingConfigEntry<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock server settings", true, "Use the server's fishing settings on every player.")); productionEnabled = Bind("Fishing", "Production enabled", value: true, "Pause fishing when off. Does not restore the original fast producer."); interval = Bind("Fishing", "Seconds per attempt", 180f, "Active daylight seconds per fishing attempt.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 3600f)); chance = Bind("Fishing", "Catch probability", 0.5f, "Chance of one size-one fish or one seafood ingredient per attempt.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f)); cap = Bind("Stock", "Fish portion limit", 40, "Pause when nearby raw fish and fish-derived foods meet this conservative portion budget.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500)); radius = Bind("Stock", "Storage radius", 32f, "Metres around the dock to count loaded containers. Player inventories and unloaded/distant storage are not included.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 64f)); try { VerifyShip(); harmony = new Harmony("vercadi.BalancedFisherman"); harmony.Patch((MethodBase)AccessTools.Method(typeof(Container), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ContainerAwake", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Container), "OnDestroyed", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "ContainerDestroyed", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Container), "GetHoverText", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "Hover", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(OdinFishingDock), "UpdateFishProduction", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "Production", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ACTIVE: original dock production suppressed; balanced catches wait for world data. Matching helper required on clients and server."); } catch (Exception ex) { Faulted = true; Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } ((Behaviour)this).enabled = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Balanced fishing disabled during initialization; original dock production retained. " + ex)); } } private ConfigEntry<T> Bind<T>(string section, string key, T value, string desc, AcceptableValueBase range = null) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, value, new ConfigDescription(desc, range, Array.Empty<object>())); ((ConditionalConfigSync)sync).AddConfigEntry<T>(val, (ConfigSyncMode)0); return val; } private static void VerifyShip() { if (!Chainloader.PluginInfos.TryGetValue("marlthon.OdinShipPlus", out var value) || !Compatibility.SupportedVersion(value.Metadata.Version)) { throw new NotSupportedException("This release requires OdinShipPlus 0.8.5. Newer versions need a compatibility review."); } Compatibility.RequireApi(typeof(OdinFishingDock), typeof(Container), typeof(ZNetView), typeof(GameObject)); } private void Update() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (Faulted || realtimeSinceStartup < nextRegistryCheck) { return; } nextRegistryCheck = realtimeSinceStartup + 3f; if (!Object.op_Implicit((Object)(object)ObjectDB.instance) || !Object.op_Implicit((Object)(object)ZNetScene.instance) || !Object.op_Implicit((Object)(object)ZoneSystem.instance) || ObjectDB.instance.m_recipes.Count == 0) { string text = "Waiting for world data: ObjectDB=" + Object.op_Implicit((Object)(object)ObjectDB.instance) + ", scene=" + Object.op_Implicit((Object)(object)ZNetScene.instance) + ", zone=" + Object.op_Implicit((Object)(object)ZoneSystem.instance) + ", recipes=" + (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.m_recipes.Count : 0); if (text != waiting) { ((BaseUnityPlugin)this).Logger.LogInfo((object)text); waiting = text; } Registry = null; registryReadyAt = 0f; } else { if ((Object)(object)ObjectDB.instance == (Object)(object)preparedDb && (Object)(object)ZNetScene.instance == (Object)(object)preparedScene) { return; } Registry = null; if (registryReadyAt == 0f) { registryReadyAt = realtimeSinceStartup + 10f; } else if (!(realtimeSinceStartup < registryReadyAt)) { try { Registry = CatchRegistry.Build(); preparedDb = ObjectDB.instance; preparedScene = ZNetScene.instance; registryReadyAt = 0f; ((BaseUnityPlugin)this).Logger.LogInfo((object)("READY: " + Registry.Options.Count + " reviewed catches; " + Registry.Weights.Count + " raw/processed stock mappings. No villagers or additional creatures added.")); Registry.Export(Path.Combine(Paths.BepInExRootPath, "BalancedFisherman-catalog.tsv")); } catch (Exception ex) { Faulted = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Fishing paused: catch/recipe registration failed. " + ex)); } } } } private static bool Production(OdinFishingDock __instance) { if ((Object)(object)Instance == (Object)null) { return false; } try { Instance.Tick(__instance); } catch (Exception ex) { Instance.Faulted = true; ((BaseUnityPlugin)Instance).Logger.LogError((object)("Fishing paused after an unexpected error; no automatic retry. " + ex)); } return false; } private void Tick(OdinFishingDock dock) { //IL_0136: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)dock)) { return; } bool flag = Object.op_Implicit((Object)(object)EnvMan.instance) && EnvMan.IsDaylight(); if (Object.op_Implicit((Object)(object)dock.m_productionEffect)) { dock.m_productionEffect.SetActive(flag); } ZNetView component = ((Component)dock).GetComponent<ZNetView>(); Clock value = Clocks.GetValue(dock, (OdinFishingDock _) => new Clock()); float realtimeSinceStartup = Time.realtimeSinceStartup; bool flag2 = Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner(); double elapsed = ((value.Owner && value.Active) ? (realtimeSinceStartup - value.Last) : 0f); value.Last = realtimeSinceStartup; value.Owner = flag2; value.Active = false; if (!flag2) { return; } ZDO zDO = component.GetZDO(); zDO.Set("lastTime", ZNet.instance.GetTime().Ticks); zDO.Set("production_progress", 0f); string text = (Faulted ? "Paused: check mod log" : ((!productionEnabled.Value) ? "Paused" : ((Registry == null) ? "Preparing fishing supplies" : ((!flag) ? "Resting until daylight" : ((!Object.op_Implicit((Object)(object)Player.GetClosestPlayer(((Component)dock).transform.position, 64f))) ? "Waiting for a nearby player" : null))))); Container targetContainer = dock.m_targetContainer; if (text == null && (!Object.op_Implicit((Object)(object)targetContainer) || targetContainer.IsInUse())) { text = "Waiting for the dock chest to close"; } ZNetView val = (Object.op_Implicit((Object)(object)targetContainer) ? Access.View(targetContainer) : null); if (text == null && (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !val.IsOwner())) { text = "Waiting for chest synchronization"; } if (text != null) { SetStatus(zDO, text); return; } Access.Load(targetContainer); Inventory inventory = targetContainer.GetInventory(); if (inventory == null || !Access.Current(targetContainer)) { SetStatus(zDO, "Waiting for chest synchronization"); return; } if (!TryStock(dock, targetContainer, out var total)) { SetStatus(zDO, "Waiting for nearby storage"); return; } ItemData val2; int num2; bool num3; int num4; checked { long limit = unchecked((long)cap.Value) * 1000L; if (!Rules.Fits(total, limit, 1000L)) { SetStatus(zDO, "Fish stocks full (" + Math.Ceiling((double)total / 1000.0) + "/" + cap.Value + ")"); return; } Catch[] array = Registry.Options.Where((Catch c) => (c.Key.Length == 0 || ZoneSystem.instance.GetGlobalKey(c.Key)) && inventory.CanAddItem(Registry.Items[c.Prefab], 1)).ToArray(); if (array.Length == 0) { SetStatus(zDO, "Dock chest full or catches locked"); return; } value.Active = true; double num = Rules.Advance(zDO.GetFloat("vercadi_fisher_progress", 0f), elapsed, interval.Value, active: true); zDO.Set("vercadi_fisher_progress", (float)num); if (num < (double)interval.Value) { SetStatus(zDO, "Fishing: next attempt in " + Math.Ceiling((double)interval.Value - num) + "s"); return; } zDO.Set("vercadi_fisher_progress", 0f); if (Random.value >= chance.Value) { SetStatus(zDO, "No catch this time"); return; } string name = Rules.Choose(array, Math.Min(Random.value, (float)Math.PI * 113f / 355f)); if (!component.IsOwner() || !val.IsOwner() || targetContainer.IsInUse() || !Access.Current(targetContainer)) { return; } val2 = Registry.Items[name].GetComponent<ItemDrop>().m_itemData.Clone(); val2.m_stack = 1; val2.m_quality = 1; val2.m_variant = 0; val2.m_dropPrefab = Registry.Items[name]; num2 = (from i in inventory.GetAllItems() where Object.op_Implicit((Object)(object)i.m_dropPrefab) && ((Object)i.m_dropPrefab).name == name select i).Sum((ItemData i) => i.m_stack); if (!inventory.CanAddItem(val2, 1)) { SetStatus(zDO, "Dock chest full"); return; } num3 = inventory.AddItem(val2); num4 = (from i in inventory.GetAllItems() where Object.op_Implicit((Object)(object)i.m_dropPrefab) && ((Object)i.m_dropPrefab).name == name select i).Sum((ItemData i) => i.m_stack); } if (!num3 || num4 != num2 + 1) { throw new InvalidOperationException("Fish insertion did not produce exactly one item."); } Access.Save(targetContainer); if (!Access.Current(targetContainer)) { throw new InvalidOperationException("Fish chest persistence did not verify."); } SetStatus(zDO, "Caught: " + Localization.instance.Localize(val2.m_shared.m_name)); } private bool TryStock(OdinFishingDock dock, Container target, out long total) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown Containers.RemoveWhere((Container c) => !Object.op_Implicit((Object)(object)c)); total = 0L; HashSet<ZDOID> hashSet = new HashSet<ZDOID>(); List<Container> list = Containers.Where(delegate(Container c) { //IL_001c: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)c)) { if (!((Object)(object)c == (Object)(object)target)) { Vector3 val3 = ((Component)c).transform.position - ((Component)dock).transform.position; return ((Vector3)(ref val3)).sqrMagnitude <= radius.Value * radius.Value; } return true; } return false; }).ToList(); if (!list.Contains(target)) { list.Add(target); } checked { foreach (Container item in list) { ZNetView val = Access.View(item); if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !hashSet.Add(val.GetZDO().m_uid)) { continue; } if (item.IsInUse()) { return false; } byte[] byteArray = val.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray == null) { if (item.GetInventory() != null && item.GetInventory().NrOfItems() > 0) { return false; } continue; } StockSnapshot value = stockCache.GetValue(item, (Container _) => new StockSnapshot()); if (value.Registry != Registry || value.Data == null || !value.Data.SequenceEqual(byteArray)) { Inventory val2 = new Inventory("Fisherman stock snapshot", (Sprite)null, Math.Max(1, item.m_width), Math.Max(1, item.m_height)); val2.Load(new ZPackage(byteArray)); value.Count = Rules.Stock(from i in val2.GetAllItems() select new KeyValuePair<string, int>(Registry.Name(i), i.m_stack), Registry.Weights); value.Data = (byte[])byteArray.Clone(); value.Registry = Registry; } total += value.Count; } return true; } } private static void SetStatus(ZDO z, string text) { if (z.GetString("vercadi_fisher_status", "") != text) { z.Set("vercadi_fisher_status", text); } } private static void ContainerAwake(Container __instance) { if (Object.op_Implicit((Object)(object)__instance)) { Instance?.Containers.Add(__instance); } } private static void ContainerDestroyed(Container __instance) { Instance?.Containers.Remove(__instance); } private static void Hover(Container __instance, ref string __result) { if (!Object.op_Implicit((Object)(object)__instance)) { return; } OdinFishingDock componentInParent = ((Component)__instance).GetComponentInParent<OdinFishingDock>(); if (!Object.op_Implicit((Object)(object)componentInParent)) { return; } ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid()) { string text = component.GetZDO().GetString("vercadi_fisher_status", ""); if (text.Length > 0) { __result = __result + "\n" + text; } } } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } internal static class Access { private static readonly FieldInfo ViewField = AccessTools.Field(typeof(Container), "m_nview"); private static readonly MethodInfo LoadMethod = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); private static readonly MethodInfo SaveMethod = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null); internal static ZNetView View(Container c) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (ZNetView)ViewField.GetValue(c); } internal static void Load(Container c) { LoadMethod.Invoke(c, null); } internal static void Save(Container c) { SaveMethod.Invoke(c, null); } internal static bool Current(Container c) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown ZNetView val = View(c); if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !val.IsOwner() || c.IsInUse()) { return false; } byte[] byteArray = val.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray == null) { return c.GetInventory().NrOfItems() == 0; } ZPackage val2 = new ZPackage(); c.GetInventory().Save(val2); return val2.GetArray().SequenceEqual(byteArray); } } internal sealed class Catch { internal readonly string Prefab; internal readonly string Key; internal readonly int Weight; internal Catch(string prefab, int weight, string key = "") { Prefab = prefab; Weight = weight; Key = key; } } internal sealed class Conversion { internal string Output; internal int Amount; internal bool Any; internal Dictionary<string, int> Inputs = new Dictionary<string, int>(StringComparer.Ordinal); } internal static class Rules { internal const long Unit = 1000L; internal static readonly Catch[] Catalog = new Catch[24] { new Catch("Fish1", 22), new Catch("Fish2", 16), new Catch("Fish3", 10, "defeated_eikthyr"), new Catch("Fish5", 10, "defeated_eikthyr"), new Catch("Fish6", 7, "defeated_gdking"), new Catch("Fish4_cave", 3, "defeated_bonemass"), new Catch("Fish7", 6, "defeated_bonemass"), new Catch("Fish8", 4, "defeated_dragon"), new Catch("Fish9", 3, "defeated_goblinking"), new Catch("Fish12", 2, "defeated_goblinking"), new Catch("Fish11", 2, "defeated_queen"), new Catch("Fish10", 1, "defeated_fader"), new Catch("Item_Shrimp_RtD", 10), new Catch("Meat_Crab_RtD", 8), new Catch("Meat_Cod_RtD", 8, "defeated_eikthyr"), new Catch("Meat_Tuna_RtD", 7, "defeated_eikthyr"), new Catch("Meat_Squid_RtD", 5, "defeated_gdking"), new Catch("Meat_Manta_RtD", 3, "defeated_gdking"), new Catch("Meat_Shark_RtD", 2, "defeated_bonemass"), new Catch("Meat_Marlin_RtD", 3, "defeated_bonemass"), new Catch("Meat_Turtle_RtD", 1, "defeated_goblinking"), new Catch("Meat_Whale_RtD", 1, "defeated_dragon"), new Catch("SharkMeatRaw_RtD", 1, "defeated_dragon"), new Catch("SeaMonsterTailRaw_RtD", 1, "defeated_queen") }; internal static bool Valid(double v) { if (!double.IsNaN(v)) { return !double.IsInfinity(v); } return false; } internal static double Advance(double progress, double elapsed, double interval, bool active) { if (!Valid(interval) || interval < 30.0) { throw new ArgumentOutOfRangeException("interval"); } if (!Valid(progress) || progress < 0.0 || progress >= interval) { progress = 0.0; } if (!active || !Valid(elapsed) || elapsed < 0.0 || elapsed > 20.0) { return progress; } return Math.Min(interval, progress + elapsed); } internal static string Choose(IEnumerable<Catch> options, double sample) { if (!Valid(sample) || sample < 0.0 || sample >= 1.0) { throw new ArgumentOutOfRangeException("sample"); } Catch[] array = options.ToArray(); if (array.Any((Catch x) => x == null || x.Weight <= 0) || array.Select((Catch x) => x.Prefab).Distinct().Count() != array.Length) { throw new ArgumentException("Invalid catch table."); } long num = array.Aggregate(0L, (long n, Catch x) => checked(n + x.Weight)); if (num == 0L) { return null; } double num2 = sample * (double)num; Catch[] array2 = array; foreach (Catch obj in array2) { num2 -= (double)obj.Weight; if (num2 < 0.0) { return obj.Prefab; } } return array.Last().Prefab; } internal static HashSet<string> FoodChain(IEnumerable<string> seeds, IEnumerable<string> food, IEnumerable<Conversion> stations) { HashSet<string> hashSet = new HashSet<string>(seeds.Concat(food), StringComparer.Ordinal); Conversion[] array = stations.ToArray(); bool flag; do { flag = false; Conversion[] array2 = array; foreach (Conversion conversion in array2) { if (!hashSet.Contains(conversion.Output)) { continue; } foreach (string key in conversion.Inputs.Keys) { flag |= hashSet.Add(key); } } } while (flag); hashSet.RemoveWhere((string x) => x == "Feathers" || x.StartsWith("FishingBait", StringComparison.Ordinal)); return hashSet; } internal static Dictionary<string, long> FoodWeights(IEnumerable<string> seeds, IEnumerable<Conversion> conversions) { HashSet<string> hashSet = new HashSet<string>(seeds, StringComparer.Ordinal); Dictionary<string, long> dictionary = hashSet.ToDictionary<string, string, long>((string x) => x, (string x) => 1000L, StringComparer.Ordinal); Conversion[] array = conversions.ToArray(); for (int num = 0; num < 64; num++) { bool flag = false; Conversion[] array2 = array; foreach (Conversion conversion in array2) { if (conversion == null || string.IsNullOrEmpty(conversion.Output) || conversion.Amount <= 0 || conversion.Inputs.Any((KeyValuePair<string, int> x) => x.Value < 0)) { throw new ArgumentException("Invalid food conversion."); } if (hashSet.Contains(conversion.Output)) { continue; } long num3 = 0L; checked { foreach (KeyValuePair<string, int> input in conversion.Inputs) { if (dictionary.TryGetValue(input.Key, out var value)) { long num4 = value * input.Value; num3 = (conversion.Any ? Math.Max(num3, num4) : (num3 + num4)); } } } if (num3 != 0L) { long num5 = checked(num3 + conversion.Amount - 1) / conversion.Amount; if (!dictionary.TryGetValue(conversion.Output, out var value2) || num5 > value2) { dictionary[conversion.Output] = num5; flag = true; } } } if (!flag) { return dictionary; } } throw new InvalidOperationException("Fish recipe cycle did not converge; production paused."); } internal static long Stock(IEnumerable<KeyValuePair<string, int>> items, IReadOnlyDictionary<string, long> weights) { long num = 0L; foreach (KeyValuePair<string, int> item in items) { if (item.Value < 0) { throw new ArgumentException("Negative stack."); } if (weights.TryGetValue(item.Key, out var value)) { num = checked(num + value * item.Value); } } return num; } internal static bool Fits(long current, long limit, long incoming) { if (current >= 0 && limit > 0 && incoming > 0 && current <= limit) { return incoming <= limit - current; } return false; } } }