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 System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SunhavenMods.Shared; using TrinketFortune.Patches; using UnityEngine; using UnityEngine.SceneManagement; using Wish; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("TrinketFortune")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+79057c8571b97b27a9323b455b8f88a51829b688")] [assembly: AssemblyProduct("TrinketFortune")] [assembly: AssemblyTitle("TrinketFortune")] [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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 SunhavenMods.Shared { public static class ConfigFileHelper { public static ConfigFile CreateNamedConfig(string pluginGuid, string configFileName, Action logWarning = null) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, configFileName); string text2 = Path.Combine(Paths.ConfigPath, pluginGuid + ".cfg"); try { if (!File.Exists(text) && File.Exists(text2)) { File.Copy(text2, text); } } catch (Exception ex) { logWarning?.Invoke("[Config] Migration to " + configFileName + " failed: " + ex.Message); } return new ConfigFile(text, true); } public static bool ReplacePluginConfig(BaseUnityPlugin plugin, ConfigFile newConfig, Action logWarning = null) { if ((Object)(object)plugin == (Object)null || newConfig == null) { return false; } try { Type typeFromHandle = typeof(BaseUnityPlugin); PropertyInfo property = typeFromHandle.GetProperty("Config", BindingFlags.Instance | BindingFlags.Public); if (property != null && property.CanWrite) { property.SetValue(plugin, newConfig, null); return true; } FieldInfo field = typeFromHandle.GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(plugin, newConfig); return true; } FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(ConfigFile)) { fieldInfo.SetValue(plugin, newConfig); return true; } } } catch (Exception ex) { logWarning?.Invoke("[Config] ReplacePluginConfig failed: " + ex.Message); } return false; } } public static class SharedCodeRevision { public const string Value = "2026.07.28"; } public sealed class ModHealthReport { public string PluginGuid { get; set; } public string DisplayName { get; set; } public string PluginVersion { get; set; } public string SharedCodeRevision { get; set; } public string IntegrationSummary { get; set; } public string Mode { get; set; } public string CharacterName { get; set; } public bool? DataLoaded { get; set; } public string LastPersistenceOutcome { get; set; } public string LastError { get; set; } public DateTime ReportedUtc { get; set; } public ModHealthReport Clone() { return new ModHealthReport { PluginGuid = PluginGuid, DisplayName = DisplayName, PluginVersion = PluginVersion, SharedCodeRevision = SharedCodeRevision, IntegrationSummary = IntegrationSummary, Mode = Mode, CharacterName = CharacterName, DataLoaded = DataLoaded, LastPersistenceOutcome = LastPersistenceOutcome, LastError = LastError, ReportedUtc = ReportedUtc }; } public void AppendDiagnosticDump(StringBuilder sb) { if (sb != null) { sb.AppendLine("guid: " + (PluginGuid ?? "—")); sb.AppendLine("name: " + (DisplayName ?? "—")); sb.AppendLine("version: " + (PluginVersion ?? "—")); sb.AppendLine("shared: " + (SharedCodeRevision ?? "—")); sb.AppendLine("integrations: " + (IntegrationSummary ?? "—")); sb.AppendLine("mode: " + (Mode ?? "—")); sb.AppendLine("character: " + (string.IsNullOrEmpty(CharacterName) ? "—" : CharacterName)); sb.AppendLine("data loaded: " + (DataLoaded.HasValue ? DataLoaded.Value.ToString() : "—")); sb.AppendLine("last save: " + (LastPersistenceOutcome ?? "—")); sb.AppendLine("last error: " + (LastError ?? "—")); sb.AppendLine("reported: " + ((ReportedUtc == default(DateTime)) ? "—" : ReportedUtc.ToLocalTime().ToString("u"))); } } } public static class ModDiagnostics { private static readonly Dictionary ReportsByPluginGuid = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly object Lock = new object(); public static void ReportStartup(ModHealthReport report) { if (report == null || string.IsNullOrWhiteSpace(report.PluginGuid)) { return; } report.ReportedUtc = DateTime.UtcNow; if (string.IsNullOrEmpty(report.SharedCodeRevision)) { report.SharedCodeRevision = "2026.07.28"; } lock (Lock) { ReportsByPluginGuid[report.PluginGuid] = report.Clone(); } } public static void ReportRuntime(string pluginGuid, Action update) { if (string.IsNullOrWhiteSpace(pluginGuid) || update == null) { return; } lock (Lock) { if (!ReportsByPluginGuid.TryGetValue(pluginGuid, out ModHealthReport value)) { value = new ModHealthReport { PluginGuid = pluginGuid }; ReportsByPluginGuid[pluginGuid] = value; } update(value); value.ReportedUtc = DateTime.UtcNow; } } public static ModHealthReport GetReport(string pluginGuid) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return null; } lock (Lock) { ModHealthReport value; return ReportsByPluginGuid.TryGetValue(pluginGuid, out value) ? value.Clone() : null; } } public static IReadOnlyList GetAllReports() { lock (Lock) { return ReportsByPluginGuid.Values.Select((ModHealthReport r) => r.Clone()).ToList(); } } public static string FormatHealthLogLine(ModHealthReport report) { if (report == null) { return "[Health] (empty report)"; } string text = (string.IsNullOrEmpty(report.DisplayName) ? report.PluginGuid : report.DisplayName); string text2 = (string.IsNullOrEmpty(report.PluginVersion) ? "?" : report.PluginVersion); string text3 = (string.IsNullOrEmpty(report.SharedCodeRevision) ? "?" : report.SharedCodeRevision); string text4 = (string.IsNullOrEmpty(report.IntegrationSummary) ? "—" : report.IntegrationSummary); string text5 = (string.IsNullOrEmpty(report.Mode) ? "—" : report.Mode); string text6 = (string.IsNullOrEmpty(report.CharacterName) ? "—" : report.CharacterName); string text7 = ((!report.DataLoaded.HasValue) ? "—" : (report.DataLoaded.Value ? "loaded" : "none")); string text8 = (string.IsNullOrEmpty(report.LastPersistenceOutcome) ? "—" : report.LastPersistenceOutcome); return "[Health] " + text + " v" + text2 + " | shared " + text3 + " | integrations: " + text4 + " | mode: " + text5 + " | character: " + text6 + " | data: " + text7 + " | save: " + text8; } public static void LogStartupHealth(ManualLogSource log, ModHealthReport report) { ReportStartup(report); if (log != null) { log.LogInfo((object)FormatHealthLogLine(report)); } } public static void LogModStartup(ManualLogSource log, string pluginGuid, string displayName, string pluginVersion, string integrationSummary, string mode = "startup", bool? dataLoaded = false, string lastPersistenceOutcome = "—") { LogStartupHealth(log, new ModHealthReport { PluginGuid = pluginGuid, DisplayName = displayName, PluginVersion = pluginVersion, SharedCodeRevision = "2026.07.28", IntegrationSummary = integrationSummary, Mode = mode, DataLoaded = dataLoaded, LastPersistenceOutcome = lastPersistenceOutcome }); } } public static class SuitePluginGuids { public const string DevTools = "com.azraelgodking.havendevtools"; public const string SenpaisChest = "com.azraelgodking.senpaischest"; public const string BirthdayReminder = "com.azraelgodking.squirrelsbirthdayreminder"; public const string HavensBirthright = "com.azraelgodking.havensbirthright"; public const string Smut = "com.azraelgodking.sunhavenmuseumutilitytracker"; public const string SunhavenTodo = "com.azraelgodking.sunhaventodo"; public const string TheVault = "com.azraelgodking.thevault"; public const string HavensAlmanac = "com.azraelgodking.havensalmanac"; public const string FasterRaces = "com.azraelgodking.fasterraces"; public const string TrinketFortune = "com.azraelgodking.trinketfortune"; public const string CropOptimizer = "com.azraelgodking.cropoptimizer"; public const string HavensRespec = "com.azraelgodking.havensrespec"; public const string GiftingAssistant = "com.azraelgodking.giftingassistant"; } public static class ModHealthIntegrationSummary { public static string Build(params (string label, string pluginGuid)[] integrations) { if (integrations == null || integrations.Length == 0) { return "standalone"; } Dictionary pluginInfos = Chainloader.PluginInfos; if (pluginInfos == null) { return "standalone"; } List list = new List(integrations.Length); for (int i = 0; i < integrations.Length; i++) { var (text, text2) = integrations[i]; if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2) && pluginInfos.ContainsKey(text2)) { list.Add(text); } } if (list.Count != 0) { return string.Join(", ", list); } return "standalone"; } } } namespace TrinketFortune { public static class Config { public static ConfigEntry Enabled; public static ConfigEntry MuseumProgressBonusPercent; public static ConfigEntry MinimumMuseumProgress; public static ConfigEntry MaxBonusChancePercent; public static void Bind(ConfigFile config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown Enabled = config.Bind("General", "Enabled", true, "Enable Trinket Fortune. When enabled, odds of unowned fishing trinkets increase as you complete the aquarium."); MuseumProgressBonusPercent = config.Bind("General", "MuseumProgressBonusPercent", 5f, new ConfigDescription("Bonus to unowned trinket/fish odds per 10% aquarium completion (e.g. 5 = +5% per 10%). Applied on top of base drop chance.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 50f), Array.Empty())); MinimumMuseumProgress = config.Bind("General", "MinimumMuseumProgress", 0.2f, new ConfigDescription("Museum progress below this has no bonus (0.2 = 20% donated). Prevents bonus at very low completion.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); MaxBonusChancePercent = config.Bind("General", "MaxBonusChancePercent", 75f, new ConfigDescription("Maximum bonus chance cap (%). Clamps the computed bonus regardless of aquarium progress. E.g. 75 = never more than a 75% bonus chance.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), Array.Empty())); } } public static class DonationHelper { private const string SmutGuid = "com.azraelgodking.sunhavenmuseumutilitytracker"; private static object _donationManager; private static MethodInfo _hasDonatedByGameId; private static MethodInfo _getSectionStatsMethod; private static MethodInfo _getAllSectionsMethod; private static object _aquariumSection; private static float _lastResolveAttemptRealtime; private const float ResolveRetrySeconds = 10f; private static FieldInfo _lootLevelField; private static PropertyInfo _lootLevelProperty; private static bool _lootLevelResolved; private static bool _lootLevelLoggedMissing; public static bool IsAvailable { get { EnsureResolved(); if (_donationManager != null) { return _hasDonatedByGameId != null; } return false; } } static DonationHelper() { _lastResolveAttemptRealtime = -100f; EnsureResolved(force: true); } private static void EnsureResolved(bool force = false) { if (!force && _donationManager != null && _hasDonatedByGameId != null) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!force && realtimeSinceStartup - _lastResolveAttemptRealtime < 10f) { return; } _lastResolveAttemptRealtime = realtimeSinceStartup; if (Chainloader.PluginInfos == null || !Chainloader.PluginInfos.TryGetValue("com.azraelgodking.sunhavenmuseumutilitytracker", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { return; } try { MethodInfo method = ((object)value.Instance).GetType().GetMethod("GetDonationManager", BindingFlags.Static | BindingFlags.Public); if (!(method == null)) { _donationManager = method.Invoke(null, null); if (_donationManager != null) { _hasDonatedByGameId = _donationManager.GetType().GetMethod("HasDonatedByGameId", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(int) }, null); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("DonationHelper: SMUT bind failed: " + ex.Message)); } } } public static bool HasDonatedByGameId(int gameItemId) { EnsureResolved(); if (_donationManager == null || _hasDonatedByGameId == null) { return false; } try { return (bool)_hasDonatedByGameId.Invoke(_donationManager, new object[1] { gameItemId }); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"DonationHelper: HasDonatedByGameId failed for {gameItemId}: {ex.Message}"); } return false; } } public static int PickBiasedFishingMuseumItem() { if (!Config.Enabled.Value) { return 0; } List fishingMuseumItems = FishingRod.fishingMuseumItems; if (fishingMuseumItems == null || fishingMuseumItems.Count == 0) { return 0; } if (!IsAvailable) { return 0; } List list = new List(fishingMuseumItems.Count); for (int i = 0; i < fishingMuseumItems.Count; i++) { int num = fishingMuseumItems[i]; if (!HasDonatedByGameId(num)) { list.Add(num); } } if (list.Count == 0) { return fishingMuseumItems[Random.Range(0, fishingMuseumItems.Count)]; } float aquariumProgress = GetAquariumProgress(); if (aquariumProgress < Config.MinimumMuseumProgress.Value * 100f) { return fishingMuseumItems[Random.Range(0, fishingMuseumItems.Count)]; } float num2 = Mathf.Min(Config.MuseumProgressBonusPercent.Value * (aquariumProgress / 10f), Config.MaxBonusChancePercent.Value); if (Random.Range(0f, 100f) < num2) { return list[Random.Range(0, list.Count)]; } return fishingMuseumItems[Random.Range(0, fishingMuseumItems.Count)]; } public static float GetAquariumProgressPercent() { return GetAquariumProgress(); } private static float GetAquariumProgress() { if (TryGetSmutAquariumProgress(out var percent)) { return percent; } return GetGameSaveAquariumProgress(); } private static bool TryGetSmutAquariumProgress(out float percent) { percent = 0f; EnsureResolved(); if (_donationManager == null) { return false; } try { EnsureAquariumSectionResolved(); if (_aquariumSection == null || _getSectionStatsMethod == null) { return false; } object obj = _getSectionStatsMethod.Invoke(_donationManager, new object[1] { _aquariumSection }); if (obj == null) { return false; } Type type = obj.GetType(); FieldInfo fieldInfo = type.GetField("Item1") ?? type.GetField("donated"); FieldInfo fieldInfo2 = type.GetField("Item2") ?? type.GetField("total"); if (fieldInfo == null || fieldInfo2 == null) { return false; } int num = (int)fieldInfo.GetValue(obj); int num2 = (int)fieldInfo2.GetValue(obj); if (num2 <= 0) { return false; } percent = (float)num / (float)num2 * 100f; return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("DonationHelper: SMUT aquarium progress failed: " + ex.Message)); } return false; } } private static void EnsureAquariumSectionResolved() { if ((_aquariumSection != null && _getSectionStatsMethod != null) || _donationManager == null) { return; } Type type = _donationManager.GetType(); _getSectionStatsMethod = type.GetMethod("GetSectionStats", BindingFlags.Instance | BindingFlags.Public); if (_getSectionStatsMethod == null) { return; } Type type2 = type.Assembly.GetType("SunHavenMuseumUtilityTracker.Data.MuseumContent"); if (type2 == null) { return; } _getAllSectionsMethod = type2.GetMethod("GetAllSections", BindingFlags.Static | BindingFlags.Public); if (!(_getAllSectionsMethod?.Invoke(null, null) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item != null && string.Equals(item.GetType().GetProperty("Id")?.GetValue(item) as string, "aquarium", StringComparison.OrdinalIgnoreCase)) { _aquariumSection = item; break; } } } private static float GetGameSaveAquariumProgress() { try { int num = 0; int num2 = 0; Type type = Type.GetType("Wish.GameSave, SunHaven.Core"); if (type == null) { return 0f; } PropertyInfo property = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public); if (property == null) { return 0f; } object value = property.GetValue(null); if (value == null) { return 0f; } MethodInfo method = type.GetMethod("GetProgressIntWorld", new Type[1] { typeof(string) }); if (method == null) { return 0f; } foreach (var item2 in MuseumCurator.aquaticMuseumProgress) { string text = item2.Item1 + "complete"; int item = item2.Item2; int num3 = (int)method.Invoke(value, new object[1] { text }); num += item; num2 += num3; } return (num > 0) ? ((float)num2 / (float)num * 100f) : 0f; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("DonationHelper: aquarium progress fallback used due to reflection error: " + ex.Message)); } return 0f; } } public static FishData PickBiasedFish(RandomFishArray array, float level) { if (!Config.Enabled.Value || array == null || array.Length == 0) { return null; } if (!IsAvailable) { return null; } List list = new List(); for (int i = 0; i < array.Length; i++) { FishLoot val = array.drops[i]; if (!((Object)(object)val?.fish == (Object)null) && IsLootEligibleAtLevel(val, level) && !HasDonatedByGameId(((ItemData)val.fish).id)) { list.Add(val.fish); } } if (list.Count == 0) { return null; } float aquariumProgress = GetAquariumProgress(); if (aquariumProgress < Config.MinimumMuseumProgress.Value * 100f) { return null; } float num = Mathf.Min(Config.MuseumProgressBonusPercent.Value * (aquariumProgress / 10f), Config.MaxBonusChancePercent.Value); if (Random.Range(0f, 100f) >= num) { return null; } return list[Random.Range(0, list.Count)]; } private static bool IsLootEligibleAtLevel(object loot, float level) { if (loot == null) { return false; } EnsureLootLevelMember(loot.GetType()); object obj = null; try { if (_lootLevelField != null) { obj = _lootLevelField.GetValue(loot); } else { if (!(_lootLevelProperty != null)) { return true; } obj = _lootLevelProperty.GetValue(loot); } } catch { return true; } if (obj == null) { return true; } try { float num = Convert.ToSingle(obj); return level + 0.001f >= num; } catch { return true; } } private static void EnsureLootLevelMember(Type lootType) { if (_lootLevelResolved || lootType == null) { return; } _lootLevelResolved = true; string[] array = new string[5] { "useLevel", "level", "minLevel", "requiredLevel", "fishingLevel" }; foreach (string name in array) { _lootLevelField = lootType.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_lootLevelField != null) { return; } _lootLevelProperty = lootType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_lootLevelProperty != null) { return; } } if (!_lootLevelLoggedMissing) { _lootLevelLoggedMissing = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[TrinketFortune] No level gate on " + lootType.Name + "; fish bias uses full drop list.")); } } } } [BepInPlugin("com.azraelgodking.trinketfortune", "Trinket Fortune", "2.1.1")] public class Plugin : BaseUnityPlugin { public static class PluginInfo { public const string PLUGIN_GUID = "com.azraelgodking.trinketfortune"; public const string PLUGIN_NAME = "Trinket Fortune"; public const string PLUGIN_VERSION = "2.1.1"; } private Harmony _harmony; private bool _applicationQuitting; public static ManualLogSource Log { get; private set; } private void Awake() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ConfigFile val = CreateNamedConfig(); ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, val, (Action)Log.LogWarning); Config.Bind(val); _harmony = new Harmony("com.azraelgodking.trinketfortune"); FishingTrinketPatches.ApplyPatches(_harmony); Log.LogInfo((object)"Trinket Fortune v2.1.1 loaded. Fishing loot bias active when S.M.U.T. is installed."); ModDiagnostics.LogModStartup(Log, "com.azraelgodking.trinketfortune", "Trinket Fortune", "2.1.1", ModHealthIntegrationSummary.Build(("DevTools", "com.azraelgodking.havendevtools"), ("SMUT", "com.azraelgodking.sunhavenmuseumutilitytracker")), "startup", false); } private void OnApplicationQuit() { _applicationQuitting = true; } private void OnDestroy() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name ?? string.Empty; string text2 = text.ToLowerInvariant(); if (_applicationQuitting || !Application.isPlaying || text2.Contains("menu") || text2.Contains("title")) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[Lifecycle] Plugin OnDestroy during expected teardown (scene: " + text + ")")); } } else { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("[Lifecycle] Plugin OnDestroy outside expected teardown (scene: " + text + ")")); } } Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static ConfigFile CreateNamedConfig() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "TrinketFortune.cfg"); string text2 = Path.Combine(Paths.ConfigPath, "com.azraelgodking.trinketfortune.cfg"); try { if (!File.Exists(text) && File.Exists(text2)) { File.Copy(text2, text); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[Config] Migration to TrinketFortune.cfg failed: " + ex.Message)); } } return new ConfigFile(text, true); } public static string GetDevToolsSummary() { if (Config.Enabled == null) { return "Not ready"; } bool flag = Chainloader.PluginInfos != null && Chainloader.PluginInfos.ContainsKey("com.azraelgodking.sunhavenmuseumutilitytracker"); string text = (DonationHelper.IsAvailable ? "S.M.U.T. donations linked" : (flag ? "S.M.U.T. installed (load a save for donation data)" : "standalone mode")); if (!Config.Enabled.Value) { return "Disabled in config | " + text; } float aquariumProgressPercent = DonationHelper.GetAquariumProgressPercent(); return $"Aquarium: {aquariumProgressPercent:F1}% | Bonus {Config.MuseumProgressBonusPercent.Value}% per 10% | " + $"Min {Config.MinimumMuseumProgress.Value:P0} | Cap {Config.MaxBonusChancePercent.Value}% | {text}"; } } } namespace TrinketFortune.Patches { public static class FishingTrinketPatches { public static void ApplyPatches(Harmony harmony) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown MethodInfo methodInfo = null; MethodInfo[] methods = typeof(Utilities).GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name != "RandomItem" || !methodInfo2.IsGenericMethodDefinition) { continue; } ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters.Length != 1) { continue; } Type parameterType = parameters[0].ParameterType; if (parameterType.IsGenericType && !(parameterType.GetGenericTypeDefinition() != typeof(IList<>))) { try { methodInfo = methodInfo2.MakeGenericMethod(typeof(int)); } catch { } if (methodInfo != null) { break; } } } if (methodInfo != null) { HarmonyMethod val = new HarmonyMethod(typeof(FishingTrinketPatches), "RandomItem_Prefix", (Type[])null); harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Patched Utilities.RandomItem for fishing museum items"); } else { Plugin.Log.LogWarning((object)"Could not find Utilities.RandomItem for museum item bias"); } MethodInfo method = typeof(RandomFishArray).GetMethod("RandomItem", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(float) }, null); if (method != null) { harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(FishingTrinketPatches), "RandomFishArray_RandomItem_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Patched RandomFishArray.RandomItem for fish bias"); } else { Plugin.Log.LogWarning((object)"Could not find RandomFishArray.RandomItem"); } } private static bool RandomItem_Prefix(IList list, ref T __result) { if (!Config.Enabled.Value) { return true; } if (typeof(T) != typeof(int)) { return true; } if (list == null || list != FishingRod.fishingMuseumItems) { return true; } int num = DonationHelper.PickBiasedFishingMuseumItem(); if (num == 0) { return true; } __result = (T)(object)num; return false; } private static void RandomFishArray_RandomItem_Postfix(RandomFishArray __instance, float level, ref FishData __result) { FishData val = DonationHelper.PickBiasedFish(__instance, level); if ((Object)(object)val != (Object)null) { __result = val; } } } }