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 System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CropOptimizer.Config; using CropOptimizer.Data; using CropOptimizer.Integration; using CropOptimizer.Patches; using CropOptimizer.UI; using HarmonyLib; using Microsoft.CodeAnalysis; using SunhavenMods.Shared; using TMPro; using TheVault.Modding; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; 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("CropOptimizer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+dbdcb88891adceccfb15f78cab211a27ae203678")] [assembly: AssemblyProduct("CropOptimizer")] [assembly: AssemblyTitle("CropOptimizer")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 abstract class PersistentRunnerBase : MonoBehaviour { private bool _wasInGame; private float _lastHeartbeat; private string _lastSceneName = ""; private float _lastSceneCheckTime; private const float SceneCheckInterval = 0.5f; protected virtual float HeartbeatInterval => 0f; protected virtual string RunnerName => ((object)this).GetType().Name; protected virtual void OnUpdate() { } protected virtual void OnMenuTransition() { } protected virtual void OnGameTransition() { } protected virtual void Log(string message) { } protected virtual void LogWarning(string message) { } public static T CreateRunner() where T : PersistentRunnerBase { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown GameObject val = new GameObject("[" + typeof(T).Name + "]") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)val); SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(val); return val.AddComponent(); } protected virtual void Awake() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); _lastSceneName = ((Scene)(ref activeScene)).name; _wasInGame = !SceneHelpers.IsMenuScene(_lastSceneName); Log("[" + RunnerName + "] Initialized in scene: " + _lastSceneName); } protected virtual void Update() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (HeartbeatInterval > 0f) { _lastHeartbeat += Time.deltaTime; if (_lastHeartbeat >= HeartbeatInterval) { _lastHeartbeat = 0f; Log("[" + RunnerName + "] Heartbeat - still alive"); } } float unscaledTime = Time.unscaledTime; if (unscaledTime - _lastSceneCheckTime >= 0.5f) { _lastSceneCheckTime = unscaledTime; Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (name != _lastSceneName) { _lastSceneName = name; HandleSceneChange(name); } } try { OnUpdate(); } catch (Exception ex) { LogWarning("[" + RunnerName + "] Error in OnUpdate: " + ex.Message); } } private void HandleSceneChange(string sceneName) { bool flag = SceneHelpers.IsMenuScene(sceneName); if (_wasInGame && flag) { Log("[" + RunnerName + "] Menu transition detected"); try { OnMenuTransition(); } catch (Exception ex) { LogWarning("[" + RunnerName + "] Error in OnMenuTransition: " + ex.Message); } } else if (!_wasInGame && !flag) { Log("[" + RunnerName + "] Game transition detected"); try { OnGameTransition(); } catch (Exception ex2) { LogWarning("[" + RunnerName + "] Error in OnGameTransition: " + ex2.Message); } } _wasInGame = !flag; } protected virtual 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).ToLowerInvariant(); if (!Application.isPlaying || text.Contains("menu") || text.Contains("title")) { Log("[" + RunnerName + "] OnDestroy during app quit/menu unload (expected)."); } else { LogWarning("[" + RunnerName + "] OnDestroy outside quit/menu (unexpected)."); } } } public static class VersionChecker { public class VersionCheckResult { public bool Success { get; set; } public bool UpdateAvailable { get; set; } public string CurrentVersion { get; set; } public string LatestVersion { get; set; } public string ModName { get; set; } public string NexusUrl { get; set; } public string Changelog { get; set; } public string ErrorMessage { get; set; } } public class ModHealthSnapshot { public string PluginGuid { get; set; } public DateTime LastCheckUtc { get; set; } public int ExceptionCount { get; set; } public string LastError { get; set; } } private class VersionCheckRunner : MonoBehaviour { private ManualLogSource _pluginLog; public void StartCheck(string pluginGuid, string currentVersion, ManualLogSource pluginLog, Action onComplete) { _pluginLog = pluginLog; ((MonoBehaviour)this).StartCoroutine(CheckVersionCoroutine(pluginGuid, currentVersion, onComplete)); } private void LogInfo(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogInfo((object)("[VersionChecker] " + message)); } } private void LogWarningMsg(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[VersionChecker] " + message)); } } private void LogErrorMsg(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogError((object)("[VersionChecker] " + message)); } } private IEnumerator CheckVersionCoroutine(string pluginGuid, string currentVersion, Action onComplete) { VersionCheckResult result = new VersionCheckResult { CurrentVersion = currentVersion }; UnityWebRequest www = UnityWebRequest.Get("https://azraelgodking.github.io/SunhavenMod/versions.json"); try { www.timeout = 10; yield return www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { result.Success = false; result.ErrorMessage = "Network error: " + www.error; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } try { string text = www.downloadHandler.text; Match match = GetModPattern(pluginGuid).Match(text); if (!match.Success) { result.Success = false; result.ErrorMessage = "Mod '" + pluginGuid + "' not found in versions.json"; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } string value = match.Groups[1].Value; result.LatestVersion = ExtractJsonString(value, "version"); result.ModName = ExtractJsonString(value, "name"); result.NexusUrl = ExtractJsonString(value, "nexus"); result.Changelog = ExtractJsonString(value, "changelog"); if (string.IsNullOrEmpty(result.LatestVersion)) { result.Success = false; result.ErrorMessage = "Could not parse version from response"; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } result.Success = true; result.UpdateAvailable = CompareVersions(currentVersion, result.LatestVersion) < 0; if (result.UpdateAvailable) { LogInfo("Update available for " + result.ModName + ": " + currentVersion + " -> " + result.LatestVersion); } else { LogInfo(result.ModName + " is up to date (v" + currentVersion + ")"); } } catch (Exception ex) { result.Success = false; result.ErrorMessage = "Parse error: " + ex.Message; RecordHealthError(pluginGuid, result.ErrorMessage); LogErrorMsg(result.ErrorMessage); } } finally { ((IDisposable)www)?.Dispose(); } onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); } private string ExtractJsonString(string json, string key) { Match match = ExtractFieldRegex.Match(json); while (match.Success) { if (string.Equals(match.Groups["key"].Value, key, StringComparison.Ordinal)) { return match.Groups["value"].Value; } match = match.NextMatch(); } return null; } } private const string VersionsUrl = "https://azraelgodking.github.io/SunhavenMod/versions.json"; private static readonly Dictionary HealthByPluginGuid = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly object HealthLock = new object(); private static readonly Dictionary ModPatternCache = new Dictionary(StringComparer.Ordinal); private static readonly object ModPatternCacheLock = new object(); private static readonly Regex ExtractFieldRegex = new Regex("\"(?[^\"]+)\"\\s*:\\s*(?:\"(?[^\"]*)\"|null)", RegexOptions.Compiled); public static void CheckForUpdate(string pluginGuid, string currentVersion, ManualLogSource logger = null, Action onComplete = null) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) TouchHealth(pluginGuid); VersionCheckRunner versionCheckRunner = new GameObject("VersionChecker").AddComponent(); Object.DontDestroyOnLoad((Object)(object)((Component)versionCheckRunner).gameObject); SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(((Component)versionCheckRunner).gameObject); versionCheckRunner.StartCheck(pluginGuid, currentVersion, logger, onComplete); } public static ModHealthSnapshot GetHealthSnapshot(string pluginGuid) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return null; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { return null; } return new ModHealthSnapshot { PluginGuid = value.PluginGuid, LastCheckUtc = value.LastCheckUtc, ExceptionCount = value.ExceptionCount, LastError = value.LastError }; } } public static int CompareVersions(string v1, string v2) { if (string.IsNullOrEmpty(v1) || string.IsNullOrEmpty(v2)) { return 0; } v1 = v1.TrimStart('v', 'V'); v2 = v2.TrimStart('v', 'V'); int num = v1.IndexOfAny(new char[2] { '-', '+' }); if (num >= 0) { v1 = v1.Substring(0, num); } int num2 = v2.IndexOfAny(new char[2] { '-', '+' }); if (num2 >= 0) { v2 = v2.Substring(0, num2); } string[] array = v1.Split(new char[1] { '.' }); string[] array2 = v2.Split(new char[1] { '.' }); int num3 = Math.Max(array.Length, array2.Length); for (int i = 0; i < num3; i++) { int result; int num4 = ((i < array.Length && int.TryParse(array[i], out result)) ? result : 0); int result2; int num5 = ((i < array2.Length && int.TryParse(array2[i], out result2)) ? result2 : 0); if (num4 < num5) { return -1; } if (num4 > num5) { return 1; } } return 0; } private static void TouchHealth(string pluginGuid) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { value = new ModHealthSnapshot { PluginGuid = pluginGuid }; HealthByPluginGuid[pluginGuid] = value; } value.LastCheckUtc = DateTime.UtcNow; } } private static void RecordHealthError(string pluginGuid, string errorMessage) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { value = new ModHealthSnapshot { PluginGuid = pluginGuid }; HealthByPluginGuid[pluginGuid] = value; } value.LastCheckUtc = DateTime.UtcNow; value.ExceptionCount++; value.LastError = errorMessage; } } private static Regex GetModPattern(string pluginGuid) { lock (ModPatternCacheLock) { if (!ModPatternCache.TryGetValue(pluginGuid, out Regex value)) { value = new Regex("\"" + Regex.Escape(pluginGuid) + "\"\\s*:\\s*\\{([^}]+)\\}", RegexOptions.Compiled | RegexOptions.Singleline); ModPatternCache[pluginGuid] = value; } return value; } } } public static class VersionCheckerExtensions { public static void NotifyUpdateAvailable(this VersionChecker.VersionCheckResult result, ManualLogSource logger = null) { if (!result.UpdateAvailable) { return; } string text = result.ModName + " update available: v" + result.LatestVersion; try { Type type = ReflectionHelper.FindWishType("NotificationStack"); if (type != null) { Type type2 = ReflectionHelper.FindType("SingletonBehaviour`1", "Wish"); if (type2 != null) { object obj = type2.MakeGenericType(type).GetProperty("Instance")?.GetValue(null); if (obj != null) { MethodInfo method = type.GetMethod("SendNotification", new Type[5] { typeof(string), typeof(int), typeof(int), typeof(bool), typeof(bool) }); if (method != null) { method.Invoke(obj, new object[5] { text, 0, 1, false, true }); return; } } } } } catch (Exception ex) { if (logger != null) { logger.LogWarning((object)("Failed to send native notification: " + ex.Message)); } } if (logger != null) { logger.LogWarning((object)("[UPDATE AVAILABLE] " + text)); } if (!string.IsNullOrEmpty(result.NexusUrl) && logger != null) { logger.LogWarning((object)("Download at: " + result.NexusUrl)); } } } public static class SceneHelpers { private static readonly string[] MenuScenePatterns = new string[3] { "menu", "title", "bootstrap" }; private static readonly string[] ExactMenuScenes = new string[2] { "MainMenu", "Bootstrap" }; public static bool IsMenuScene(string sceneName) { if (string.IsNullOrEmpty(sceneName)) { return true; } string[] exactMenuScenes = ExactMenuScenes; foreach (string text in exactMenuScenes) { if (sceneName == text) { return true; } } string text2 = sceneName.ToLowerInvariant(); exactMenuScenes = MenuScenePatterns; foreach (string value in exactMenuScenes) { if (text2.Contains(value)) { return true; } } return false; } public static bool IsCurrentSceneMenu() { //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(); return IsMenuScene(((Scene)(ref activeScene)).name); } public static bool IsInGame() { return !IsCurrentSceneMenu(); } public static string GetCurrentSceneName() { //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(); return ((Scene)(ref activeScene)).name; } public static bool IsMainMenu() { //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(); return ((Scene)(ref activeScene)).name == "MainMenu"; } } public static class SceneRootSurvivor { private static readonly object Lock = new object(); private static readonly List NoKillSubstrings = new List(); private static Harmony _harmony; public static void TryRegisterPersistentRunnerGameObject(GameObject go) { if (!((Object)(object)go == (Object)null)) { TryAddNoKillListSubstring(((Object)go).name); } } public static void TryAddNoKillListSubstring(string nameSubstring) { if (string.IsNullOrEmpty(nameSubstring)) { return; } lock (Lock) { bool flag = false; for (int i = 0; i < NoKillSubstrings.Count; i++) { if (string.Equals(NoKillSubstrings[i], nameSubstring, StringComparison.OrdinalIgnoreCase)) { flag = true; break; } } if (!flag) { NoKillSubstrings.Add(nameSubstring); } } EnsurePatched(); } private static void EnsurePatched() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00a3: Expected O, but got Unknown if (_harmony != null) { return; } lock (Lock) { if (_harmony == null) { MethodInfo methodInfo = AccessTools.Method(typeof(Scene), "GetRootGameObjects", Type.EmptyTypes, (Type[])null); if (!(methodInfo == null)) { string text = typeof(SceneRootSurvivor).Assembly.GetName().Name ?? "Unknown"; Harmony val = new Harmony("SunhavenMods.SceneRootSurvivor." + text); val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SceneRootSurvivor), "OnGetRootGameObjectsPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony = val; } } } } private static void OnGetRootGameObjectsPostfix(ref GameObject[] __result) { if (__result == null || __result.Length == 0) { return; } List list; lock (Lock) { if (NoKillSubstrings.Count == 0) { return; } list = new List(NoKillSubstrings); } List list2 = new List(__result); for (int i = 0; i < list.Count; i++) { string noKill = list[i]; list2.RemoveAll((GameObject a) => (Object)(object)a != (Object)null && ((Object)a).name.IndexOf(noKill, StringComparison.OrdinalIgnoreCase) >= 0); } __result = list2.ToArray(); } } public static class ReflectionHelper { public static readonly BindingFlags AllBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; public static Type FindType(string typeName, params string[] namespaces) { string typeName2 = typeName; Type type = AccessTools.TypeByName(typeName2); if (type != null) { return type; } for (int i = 0; i < namespaces.Length; i++) { type = AccessTools.TypeByName(namespaces[i] + "." + typeName2); if (type != null) { return type; } } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName2 || t.FullName == typeName2); if (type != null) { return type; } } catch (ReflectionTypeLoadException) { } } return null; } public static Type FindWishType(string typeName) { return FindType(typeName, "Wish"); } public static object GetStaticValue(Type type, string memberName) { if (type == null) { return null; } try { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null && property.GetIndexParameters().Length == 0) { return property.GetValue(null); } } catch (AmbiguousMatchException) { return null; } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { return field.GetValue(null); } return null; } public static object GetSingletonInstance(Type type) { if (type == null) { return null; } string[] array = new string[5] { "Instance", "instance", "_instance", "Singleton", "singleton" }; foreach (string memberName in array) { object staticValue = GetStaticValue(type, memberName); if (staticValue != null) { return staticValue; } } return null; } public static object GetInstanceValue(object instance, string memberName) { if (instance == null) { return null; } Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null) { return property.GetValue(instance); } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { return field.GetValue(instance); } type = type.BaseType; } return null; } public static bool SetInstanceValue(object instance, string memberName, object value) { if (instance == null) { return false; } Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.SetMethod != null) { property.SetValue(instance, value); return true; } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { field.SetValue(instance, value); return true; } type = type.BaseType; } return false; } public static object InvokeMethod(object instance, string methodName, params object[] args) { if (instance == null) { return null; } Type type = instance.GetType(); Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes; MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null); if (methodInfo == null) { methodInfo = type.GetMethod(methodName, AllBindingFlags); } if (methodInfo == null) { return null; } return methodInfo.Invoke(instance, args); } public static object InvokeStaticMethod(Type type, string methodName, params object[] args) { if (type == null) { return null; } Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes; MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null); if (methodInfo == null) { methodInfo = type.GetMethod(methodName, AllBindingFlags); } if (methodInfo == null) { return null; } return methodInfo.Invoke(null, args); } public static FieldInfo[] GetAllFields(Type type) { if (type == null) { return Array.Empty(); } FieldInfo[] fields = type.GetFields(AllBindingFlags); IEnumerable second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty(); } else { IEnumerable allFields = GetAllFields(type.BaseType); second = allFields; } return fields.Concat(second).Distinct().ToArray(); } public static PropertyInfo[] GetAllProperties(Type type) { if (type == null) { return Array.Empty(); } PropertyInfo[] properties = type.GetProperties(AllBindingFlags); IEnumerable second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty(); } else { IEnumerable allProperties = GetAllProperties(type.BaseType); second = allProperties; } return (from p in properties.Concat(second) group p by p.Name into g select g.First()).ToArray(); } public static T TryGetValue(object instance, string memberName, T defaultValue = default(T)) { try { object instanceValue = GetInstanceValue(instance, memberName); if (instanceValue is T result) { return result; } if (instanceValue != null && typeof(T).IsAssignableFrom(instanceValue.GetType())) { return (T)instanceValue; } return defaultValue; } catch { return defaultValue; } } } } namespace CropOptimizer { [BepInPlugin("com.azraelgodking.cropoptimizer", "Crop Optimizer", "1.4.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private Harmony _harmony; private CropOptimizerConfig _config; internal CropForecast _forecast; private CropHUD _hud; private TodoIntegration _todoIntegration; private BirthdayIntegration _birthdayIntegration; private VaultIntegration _vaultIntegration; private bool _hudVisible = true; private bool _applicationQuitting; private bool _isVaultLoadedEventSubscribed; private EventInfo _vaultLoadedEventInfo; private Delegate _vaultLoadedHandler; public static ManualLogSource Log { get; private set; } public static Plugin Instance { get; private set; } public static bool IsDebugLoggingEnabled { get { Plugin instance = Instance; if (instance == null) { return false; } return (instance._config?.DebugLogging?.Value).GetValueOrDefault(); } } private void Awake() { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigFile val = CreateNamedConfig(); ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, val, (Action)Log.LogWarning); _config = new CropOptimizerConfig(val); if (!_config.Enabled.Value) { Log.LogInfo((object)"Crop Optimizer disabled in config."); return; } _forecast = new CropForecast(); _todoIntegration = new TodoIntegration(); _birthdayIntegration = new BirthdayIntegration(); _vaultIntegration = new VaultIntegration(); _harmony = new Harmony("com.azraelgodking.cropoptimizer"); CropGrowthPatch.Apply(_harmony, _forecast); CharacterLoadPatch.Apply(_harmony, _forecast); _hud = PersistentRunnerBase.CreateRunner(); _hud.Initialize(_forecast); _hud.SetPlacement(_config.HudPositionX.Value, _config.HudPositionY.Value); _hud.SetScale(_config.HudScale.Value); _hud.SetVisible(_config.HudEnabled.Value); _hudVisible = _config.HudEnabled.Value; _hud.PlacementChanged += OnCropHudPlacementChanged; _hud.SetHoverConfig(_config.HoverTooltipEnabled, _config.HoverTooltipMaxWorldDistance); TrySubscribeVaultLoaded(); if (_config.CheckForUpdates.Value) { VersionChecker.CheckForUpdate("com.azraelgodking.cropoptimizer", "1.4.3", Log, delegate(VersionChecker.VersionCheckResult result) { result.NotifyUpdateAvailable(Log); }); } Log.LogInfo((object)"Crop Optimizer v1.4.3 loaded"); } 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 + ")")); } } if ((Object)(object)_hud != (Object)null) { _hud.PlacementChanged -= OnCropHudPlacementChanged; } Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if (_isVaultLoadedEventSubscribed && _vaultLoadedEventInfo != null && (object)_vaultLoadedHandler != null) { _vaultLoadedEventInfo.RemoveEventHandler(null, _vaultLoadedHandler); _isVaultLoadedEventSubscribed = false; _vaultLoadedEventInfo = null; _vaultLoadedHandler = null; } } private void OnApplicationQuit() { _applicationQuitting = true; } private void OnCropHudPlacementChanged(float x, float y) { if (_config != null) { _config.HudPositionX.Value = x; _config.HudPositionY.Value = y; } } private void Update() { //IL_0022: 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) if (_config != null && !((Object)(object)_hud == (Object)null) && (int)_config.ToggleHudKey.Value != 0 && Input.GetKeyDown(_config.ToggleHudKey.Value)) { _hudVisible = !_hudVisible; _hud.SetVisible(_hudVisible); } } public static string GetHudSummary() { if (Instance?._forecast == null) { return "Not ready"; } return $"Crops: {Instance._forecast.Snapshot().Count}, Value: {Instance._forecast.GetProjectedSellTotal()}g"; } internal static List GetTopCrops(int count = 5) { return Instance?._forecast?.GetTopCropsByValue(count) ?? new List(); } private void TrySubscribeVaultLoaded() { if (_vaultIntegration == null || !_vaultIntegration.IsAvailable) { return; } try { EventInfo @event = typeof(VaultModApiBridge).GetEvent("OnVaultLoaded", BindingFlags.Static | BindingFlags.Public); if (@event == null) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)"[CropOptimizer] Vault bridge OnVaultLoaded event not found; skipping subscription."); } return; } MethodInfo method = ((object)this).GetType().GetMethod("OnVaultLoaded", BindingFlags.Instance | BindingFlags.NonPublic); if (method == null) { return; } Delegate @delegate = Delegate.CreateDelegate(@event.EventHandlerType, this, method, throwOnBindFailure: false); if ((object)@delegate == null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[CropOptimizer] Vault OnVaultLoaded handler could not be bound."); } return; } @event.AddEventHandler(null, @delegate); _isVaultLoadedEventSubscribed = true; _vaultLoadedEventInfo = @event; _vaultLoadedHandler = @delegate; if (VaultModApiBridge.Instance != null && VaultModApiBridge.Instance.IsVaultReady) { _vaultIntegration.TryRegisterProjectedValueCurrency(); } } catch (Exception ex) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)("[CropOptimizer] Failed to subscribe to Vault OnVaultLoaded: " + ex.Message)); } } } private void OnVaultLoaded() { try { _vaultIntegration?.TryRegisterProjectedValueCurrency(); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[CropOptimizer] Vault currency registration failed on OnVaultLoaded: " + ex.Message)); } } } 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, "CropOptimizer.cfg"); string text2 = Path.Combine(Paths.ConfigPath, "com.azraelgodking.cropoptimizer.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 CropOptimizer.cfg failed: " + ex.Message)); } } return new ConfigFile(text, true); } } internal static class PluginInfo { public const string PLUGIN_GUID = "com.azraelgodking.cropoptimizer"; public const string PLUGIN_NAME = "Crop Optimizer"; public const string PLUGIN_VERSION = "1.4.3"; } } namespace CropOptimizer.UI { internal static class CropHoverQuery { private static Type _cropType; private static Object[] _cachedCrops; private static float _nextCacheTime; private static Camera _cachedGameplayCamera; private static float _nextGameplayCameraSearchTime; private static Vector3 _lastHoverMouseScreen; private static Component _lastHoverCrop; private static float _nextHoverFullScanTime; private const float CropCacheRefreshSeconds = 1.5f; private const float HoverRescanMinInterval = 0.055f; private const float MouseMoveSkipScanPxSq = 9f; private const float GameplayCameraSearchCooldown = 2f; private static readonly string[] WaterMemberNames = new string[10] { "isWatered", "IsWatered", "watered", "Watered", "needsWater", "NeedsWater", "water", "Water", "hasWater", "HasWater" }; private static readonly string[] FertilizerMemberNames = new string[10] { "fertilizer", "Fertilizer", "fertilized", "Fertilized", "hasFertilizer", "HasFertilizer", "fertilizerType", "FertilizerType", "soilFertility", "SoilFertility" }; private static bool _dumpedCropMembers; private static bool _loggedTileProbe; private const BindingFlags MemberFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; private static Type CropType => _cropType ?? (_cropType = AccessTools.TypeByName("Wish.Crop")); public static Camera ResolveGameplayCamera() { if ((Object)(object)Camera.main != (Object)null && ((Behaviour)Camera.main).enabled) { _cachedGameplayCamera = Camera.main; return Camera.main; } if ((Object)(object)_cachedGameplayCamera != (Object)null && ((Behaviour)_cachedGameplayCamera).enabled && ((Component)_cachedGameplayCamera).gameObject.activeInHierarchy) { return _cachedGameplayCamera; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextGameplayCameraSearchTime) { return _cachedGameplayCamera; } Camera[] array = Object.FindObjectsOfType(); Camera val = null; Camera[] array2 = array; foreach (Camera val2 in array2) { if (!((Object)(object)val2 == (Object)null) && ((Behaviour)val2).enabled && ((Component)val2).gameObject.activeInHierarchy && ((Object)(object)val == (Object)null || val2.depth > val.depth)) { val = val2; } } _cachedGameplayCamera = val; _nextGameplayCameraSearchTime = unscaledTime + (((Object)(object)val != (Object)null) ? 2f : 0.25f); return val; } public static void InvalidateGameplayCameraCache() { _cachedGameplayCamera = null; _nextGameplayCameraSearchTime = 0f; } public static void InvalidateHoverAssist() { _lastHoverCrop = null; _nextHoverFullScanTime = 0f; } public static bool TryMouseWorldOnPlane(Camera camera, float planeZ, out Vector3 world) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) world = default(Vector3); if ((Object)(object)camera == (Object)null) { return false; } Ray val = camera.ScreenPointToRay(Input.mousePosition); Plane val2 = default(Plane); ((Plane)(ref val2))..ctor(Vector3.forward, new Vector3(0f, 0f, planeZ)); float num = default(float); if (((Plane)(ref val2)).Raycast(val, ref num)) { world = ((Ray)(ref val)).GetPoint(num); return true; } Vector3 mousePosition = Input.mousePosition; mousePosition.z = Mathf.Max(0.01f, Mathf.Abs(((Component)camera).transform.position.z)); world = camera.ScreenToWorldPoint(mousePosition); world.z = planeZ; return true; } public static bool TryGetClosestCropNearMouse(Camera camera, float maxWorldDistance, out Component crop) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_018b: 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_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) crop = null; if (CropType == null || (Object)(object)camera == (Object)null) { return false; } RefreshCropCache(); if (_cachedCrops == null || _cachedCrops.Length == 0) { return false; } float planeZ = 0f; Object[] cachedCrops = _cachedCrops; foreach (Object obj in cachedCrops) { Component val = (Component)(object)((obj is Component) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { planeZ = val.transform.position.z; break; } } if (!TryMouseWorldOnPlane(camera, planeZ, out var world)) { return false; } Vector3 mousePosition = Input.mousePosition; float unscaledTime = Time.unscaledTime; float num = maxWorldDistance * maxWorldDistance; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(world.x, world.y); Vector3 val3 = mousePosition - _lastHoverMouseScreen; bool num2 = ((Vector3)(ref val3)).sqrMagnitude <= 9f; _lastHoverMouseScreen = mousePosition; Vector2 val4; if (num2 && unscaledTime < _nextHoverFullScanTime && (Object)(object)_lastHoverCrop != (Object)null) { Component lastHoverCrop = _lastHoverCrop; if ((Object)(object)lastHoverCrop != (Object)null) { val4 = new Vector2(lastHoverCrop.transform.position.x, lastHoverCrop.transform.position.y) - val2; if (((Vector2)(ref val4)).sqrMagnitude < num) { crop = lastHoverCrop; return true; } } } _nextHoverFullScanTime = unscaledTime + 0.055f; float num3 = num; Component val5 = null; cachedCrops = _cachedCrops; foreach (Object obj2 in cachedCrops) { Component val6 = (Component)(object)((obj2 is Component) ? obj2 : null); if (val6 != null && !((Object)(object)val6 == (Object)null) && CropInstanceRegistry.IsKnownActive(((Object)val6).GetInstanceID())) { val4 = new Vector2(val6.transform.position.x, val6.transform.position.y) - val2; float sqrMagnitude = ((Vector2)(ref val4)).sqrMagnitude; if (sqrMagnitude < num3) { num3 = sqrMagnitude; val5 = val6; } } } if ((Object)(object)val5 == (Object)null) { _lastHoverCrop = null; return false; } crop = val5; _lastHoverCrop = val5; return true; } private static void RefreshCropCache() { float unscaledTime = Time.unscaledTime; if (_cachedCrops != null && unscaledTime < _nextCacheTime) { return; } _nextCacheTime = unscaledTime + 1.5f; Type cropType = CropType; if (cropType == null) { _cachedCrops = Array.Empty(); return; } Object[] array = Object.FindObjectsOfType(cropType); for (int i = 0; i < array.Length; i++) { CropInstanceRegistry.Register(array[i]); } _cachedCrops = array; } public static string FormatWaterGuess(object cropInstance) { return FormatMemberGuess(cropInstance, WaterMemberNames); } public static string FormatFertilizerGuess(object cropInstance) { return FormatMemberGuess(cropInstance, FertilizerMemberNames); } private static void LogTileDebugOnce(Component crop, Vector2Int tile) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (_loggedTileProbe) { return; } _loggedTileProbe = true; try { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)CropTileReflection.BuildDebugSnapshot(crop, tile)); } } catch { } } private static void DumpCropMembersOnce(object cropInstance) { if (_dumpedCropMembers || cropInstance == null) { return; } _dumpedCropMembers = true; try { Plugin instance = Plugin.Instance; if (instance != null) { _ = ((BaseUnityPlugin)instance).Config; } if (!((Object)(object)Plugin.Instance != (Object)null) || !IsDebugLogEnabled()) { return; } ManualLogSource log = Plugin.Log; if (log != null) { Type type = cropInstance.GetType(); log.LogInfo((object)("[HoverDebug] Dumping members of " + type.FullName)); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); foreach (FieldInfo fieldInfo in fields) { log.LogInfo((object)("[HoverDebug] field " + fieldInfo.FieldType.Name + " " + fieldInfo.Name)); } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); foreach (PropertyInfo propertyInfo in properties) { log.LogInfo((object)("[HoverDebug] prop " + propertyInfo.PropertyType.Name + " " + propertyInfo.Name)); } } } catch { } } private static bool IsDebugLogEnabled() { try { return (Object)(object)Plugin.Instance != (Object)null && Plugin.IsDebugLoggingEnabled; } catch { return false; } } private static string FormatMemberGuess(object instance, string[] names) { if (instance == null) { return "?"; } try { Type type = instance.GetType(); while (type != null) { foreach (string name in names) { FieldInfo fieldInfo = null; PropertyInfo propertyInfo = null; try { fieldInfo = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); } catch { } try { propertyInfo = ((fieldInfo == null) ? type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) : null); } catch { } if (!(fieldInfo == null) || !(propertyInfo == null)) { object obj3 = ((fieldInfo != null) ? fieldInfo.GetValue(instance) : propertyInfo.GetValue(instance, null)); if (obj3 != null) { return FormatPrimitiveGuess(obj3); } } } type = type.BaseType; } } catch { } return "?"; } private static string FormatPrimitiveGuess(object raw) { if (raw is bool) { if (!(bool)raw) { return "no"; } return "yes"; } if (raw is int num) { if (num == 0) { return "no"; } return $"yes ({num})"; } if (raw is float num2) { if (!(Math.Abs(num2) > 0.0001f)) { return "no"; } return $"yes ({num2:0.##})"; } if (raw is double num3) { if (!(Math.Abs(num3) > 0.0001)) { return "no"; } return $"yes ({num3:0.##})"; } string text = raw.ToString(); if (string.IsNullOrWhiteSpace(text)) { return "?"; } if (text.Length > 48) { return text.Substring(0, 45) + "..."; } return text; } public static TooltipContent BuildTooltipContent(Component crop, CropForecast forecast) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)crop == (Object)null) { return null; } DumpCropMembersOnce(crop); TooltipContent tooltipContent = new TooltipContent(); int itemId = 0; string title = "Crop"; if (CropGrowthPatch.TryGetTooltipHarvestItemId(crop, out itemId) && itemId > 0) { title = ((!CropGrowthPatch.TryGetItemDisplayName(itemId, out string displayName) || string.IsNullOrWhiteSpace(displayName)) ? $"Item #{itemId}" : displayName); } tooltipContent.Title = title; bool fullyGrown = false; CropGrowthPatch.TryGetTooltipFullyGrown(crop, out fullyGrown); if (fullyGrown) { tooltipContent.HeaderTag = "ready to harvest"; } if (CropGrowthPatch.TryGetTooltipQualityInfo(crop, out string label, out float multiplier) && !string.IsNullOrEmpty(label)) { tooltipContent.QualityColor = QualityColorFor(label); tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Quality, tooltipContent.QualityColor, string.Format("Quality: {0} (×{2:0.##})", label, "#B8A078", multiplier))); } if (CropGrowthPatch.TryGetTooltipGrowthStageInfo(crop, out string stageText, out float grownRatio) && !string.IsNullOrEmpty(stageText)) { tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Sprout, UiStyle.Sprout, "Growth: " + stageText + "")); } float etaHours; bool resolvedFromReflection; CropForecast.CropState state; if (fullyGrown) { tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Ready, UiStyle.Fertilizer, "Ready now")); } else if (CropGrowthPatch.TryGetTooltipEtaHours(crop, out etaHours, out resolvedFromReflection) && resolvedFromReflection) { tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Clock, UiStyle.Clock, string.Format("Ready in ~{1:0.#} h", "#F7D982", Mathf.Max(0f, etaHours)))); } else if (forecast != null && forecast.TryGetState(((Object)crop).GetInstanceID(), out state)) { tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Clock, UiStyle.Clock, string.Format("Ready in ~{1:0.#} h (cached)", "#F7D982", Mathf.Max(0f, state.NextHarvestEtaHours), "#B8A078"))); } else { tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Clock, UiStyle.Clock, "ETA unknown — grow once to calibrate")); } if (CropGrowthPatch.TryGetTooltipProjectedGold(crop, out var projectedGold, out grownRatio) && projectedGold > 0) { tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Coin, UiStyle.Coin, string.Format("~{1:N0}g at shop", "#F7D982", projectedGold))); } Vector2Int tile = default(Vector2Int); bool num = CropTileReflection.TryGetTileCoordForCrop(crop, out tile); string raw = null; if (num) { raw = CropTileReflection.DescribeFarmingTileState(tile, crop.transform.position, haveWorldPos: true, out var _); if (IsDebugLogEnabled()) { LogTileDebugOnce(crop, tile); } } var (text, iconColor) = DescribeWaterState(raw); tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Water, iconColor, text)); if (CropGrowthPatch.TryGetTooltipFertilized(crop, out var fertilized)) { string obj = (fertilized ? "Fertilized" : "Not fertilized"); tooltipContent.Rows.Add(RowSpec.Make(text: obj, icon: UiStyle.IconKind.Fertilizer, iconColor: (Color32)(fertilized ? UiStyle.Fertilizer : new Color32((byte)154, (byte)136, (byte)96, byte.MaxValue)))); } if (CropGrowthPatch.TryGetTooltipManaInfused(crop, out var manaInfused) && manaInfused) { tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Mana, UiStyle.Mana, "Mana infused")); } if (num) { tooltipContent.Rows.Add(RowSpec.Make(UiStyle.IconKind.Tile, UiStyle.Tile, string.Format("Tile ({1}, {2})", "#B8A078", ((Vector2Int)(ref tile)).x, ((Vector2Int)(ref tile)).y))); } if (itemId > 0) { List list = new List(); CropGrowthPatch.AppendItemExtraLines(itemId, list); if (list.Count > 0) { tooltipContent.Extras = string.Join(" · ", list); } } return tooltipContent; } private static Color32 QualityColorFor(string label) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) if (string.IsNullOrEmpty(label)) { return UiStyle.QualityNormal; } string text = label.ToLowerInvariant(); if (text.Contains("gold") || text.Contains("iridium")) { return UiStyle.QualityGold; } if (text.Contains("silver")) { return UiStyle.QualitySilver; } return UiStyle.QualityNormal; } private static (string text, Color32 color) DescribeWaterState(string raw) { //IL_000d: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(raw)) { return ("Water: unknown", UiStyle.Water); } string text = raw.ToLowerInvariant(); if (text.Contains("water")) { return ("Watered", UiStyle.Water); } if (text.Contains("hoed")) { return ("Hoed (dry — needs water)", new Color32((byte)201, (byte)160, (byte)112, byte.MaxValue)); } return ("Water: " + raw, UiStyle.Water); } } internal sealed class CropHUD : PersistentRunnerBase { private CropForecast _forecast; private Canvas _canvas; private GameObject _canvasGo; private CropHudView _hudView; private CropTooltipView _tooltipView; private float _scale = 1f; private bool _isVisible = true; private float _initialX = 24f; private float _initialY = 80f; private bool _hasInitialPlacement; private ConfigEntry _hoverTooltipEnabled; private ConfigEntry _hoverTooltipMaxWorldDistance; private Component _tooltipContentCrop; private TooltipContent _tooltipContentCache; private float _nextTooltipContentRebuildTime; private int _lastHudTrackedCount = -1; private long _lastHudProjectedGold = long.MinValue; private bool? _lastHudTooltipShownInUi; private const float TooltipContentRefreshSeconds = 0.22f; protected override string RunnerName => "CropHUD"; public event Action PlacementChanged; public void Initialize(CropForecast forecast) { _forecast = forecast; } public void SetHoverConfig(ConfigEntry enabled, ConfigEntry maxWorldDistance) { _hoverTooltipEnabled = enabled; _hoverTooltipMaxWorldDistance = maxWorldDistance; } public void SetPlacement(float x, float y) { _initialX = x; _initialY = y; _hasInitialPlacement = true; _hudView?.SetPlacement(x, y); } public void SetScale(float scale) { _scale = Mathf.Clamp(scale, 0.5f, 2.5f); _hudView?.SetScale(_scale); _tooltipView?.SetScale(_scale); } public void SetVisible(bool visible) { _isVisible = visible; _hudView?.SetVisible(visible && IsCharacterSessionActive()); } private static bool IsCharacterSessionActive() { if (!SceneHelpers.IsInGame()) { return false; } try { GameSave instance = SingletonBehaviour.Instance; if ((Object)(object)instance == (Object)null) { return false; } GameSaveData currentSave = instance.CurrentSave; if (currentSave == null) { return false; } return currentSave.characterData != null; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[CropHUD] Failed to determine character session state: " + ex.Message)); } return false; } } private void EnsureCanvas() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvas != (Object)null) || !((Object)(object)_canvasGo != (Object)null)) { _canvasGo = new GameObject("CropOptimizer_HUDCanvas", new Type[3] { typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Object.DontDestroyOnLoad((Object)(object)_canvasGo); _canvas = _canvasGo.GetComponent(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 5000; CanvasScaler component = _canvasGo.GetComponent(); component.uiScaleMode = (ScaleMode)1; component.referenceResolution = new Vector2(1920f, 1080f); component.matchWidthOrHeight = 0.5f; _hudView = new CropHudView(_canvasGo.transform); _hudView.PlacementChanged += delegate(float x, float y) { this.PlacementChanged?.Invoke(x, y); }; _hudView.TooltipToggleClicked += OnTooltipToggleClicked; _hudView.SetScale(_scale); if (_hasInitialPlacement) { _hudView.SetPlacement(_initialX, _initialY); } _hudView.SetVisible(_isVisible); _hudView.SetTooltipEnabled(_hoverTooltipEnabled != null && _hoverTooltipEnabled.Value); _tooltipView = new CropTooltipView(_canvasGo.transform); _tooltipView.SetScale(_scale); _tooltipView.SetVisible(visible: false); } } private void RebuildCanvas() { if ((Object)(object)_canvasGo != (Object)null) { Object.Destroy((Object)(object)_canvasGo); _canvasGo = null; _canvas = null; _hudView = null; _tooltipView = null; } EnsureCanvas(); } protected override void OnUpdate() { if (_forecast == null) { return; } EnsureCanvas(); bool flag = IsCharacterSessionActive(); if (_hudView != null) { bool flag2 = _isVisible && flag; _hudView.SetVisible(flag2); if (flag2) { int count = _forecast.Snapshot().Count; long num = _forecast.GetProjectedSellTotal(); if (count != _lastHudTrackedCount || num != _lastHudProjectedGold) { _lastHudTrackedCount = count; _lastHudProjectedGold = num; _hudView.UpdateStats(count, num); } bool flag3 = _hoverTooltipEnabled != null && _hoverTooltipEnabled.Value; if (!_lastHudTooltipShownInUi.HasValue || flag3 != _lastHudTooltipShownInUi.Value) { _lastHudTooltipShownInUi = flag3; _hudView.SetTooltipEnabled(flag3); } } else { _lastHudTrackedCount = -1; _lastHudProjectedGold = long.MinValue; _lastHudTooltipShownInUi = null; } } UpdateHoverTooltip(flag); } private void OnTooltipToggleClicked() { if (_hoverTooltipEnabled == null) { return; } _hoverTooltipEnabled.Value = !_hoverTooltipEnabled.Value; _lastHudTooltipShownInUi = _hoverTooltipEnabled.Value; _hudView?.SetTooltipEnabled(_hoverTooltipEnabled.Value); try { ConfigFile configFile = ((ConfigEntryBase)_hoverTooltipEnabled).ConfigFile; if (configFile != null) { configFile.Save(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[CropHUD] Failed to flush hover-tooltip config change: " + ex.Message)); } } } private void UpdateHoverTooltip(bool sessionLive) { //IL_0054: 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_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) if (_tooltipView == null) { return; } if (!sessionLive || _hoverTooltipEnabled == null || !_hoverTooltipEnabled.Value) { ClearTooltipContentState(); _tooltipView.SetVisible(visible: false); return; } if (_hudView != null && _hudView.IsVisible && RectTransformUtility.RectangleContainsScreenPoint(_hudView.Rect, Vector2.op_Implicit(Input.mousePosition))) { ClearTooltipContentState(); _tooltipView.SetVisible(visible: false); return; } Camera val = CropHoverQuery.ResolveGameplayCamera(); if ((Object)(object)val == (Object)null) { ClearTooltipContentState(); _tooltipView.SetVisible(visible: false); return; } float maxWorldDistance = ((_hoverTooltipMaxWorldDistance != null) ? _hoverTooltipMaxWorldDistance.Value : 5f); if (!CropHoverQuery.TryGetClosestCropNearMouse(val, maxWorldDistance, out Component crop)) { ClearTooltipContentState(); _tooltipView.SetVisible(visible: false); return; } float unscaledTime = Time.unscaledTime; bool flag = (Object)(object)crop != (Object)(object)_tooltipContentCrop || _tooltipContentCache == null || unscaledTime >= _nextTooltipContentRebuildTime; if (flag) { _tooltipContentCache = CropHoverQuery.BuildTooltipContent(crop, _forecast); _tooltipContentCrop = crop; _nextTooltipContentRebuildTime = unscaledTime + 0.22f; } if (_tooltipContentCache == null) { ClearTooltipContentState(); _tooltipView.SetVisible(visible: false); return; } _tooltipView.SetVisible(visible: true); if (flag) { _tooltipView.ApplyContent(_tooltipContentCache); } _tooltipView.SetScreenPosition(Vector2.op_Implicit(Input.mousePosition)); } private void ClearTooltipContentState() { _tooltipContentCrop = null; _tooltipContentCache = null; _nextTooltipContentRebuildTime = 0f; } protected override void OnGameTransition() { CropHoverQuery.InvalidateGameplayCameraCache(); CropHoverQuery.InvalidateHoverAssist(); ClearTooltipContentState(); RebuildCanvas(); } protected override void OnMenuTransition() { CropHoverQuery.InvalidateGameplayCameraCache(); CropHoverQuery.InvalidateHoverAssist(); ClearTooltipContentState(); _hudView?.SetVisible(visible: false); _tooltipView?.SetVisible(visible: false); } protected override void OnDestroy() { base.OnDestroy(); if ((Object)(object)_canvasGo != (Object)null) { Object.Destroy((Object)(object)_canvasGo); _canvasGo = null; _canvas = null; _hudView = null; _tooltipView = null; } } protected override void Log(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)message); } } protected override void LogWarning(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)message); } } } internal sealed class CropHudView { internal sealed class TooltipToggleButton : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler, IPointerClickHandler { public Image Background; public Color32 NormalColor; public Color32 HoverColor; public Color32 PressedColor; private GameObject _hoverLabelRoot; private TMP_Text _hoverLabelText; private string _pendingLabel = "Toggle Crop Tooltips"; private bool _hovered; private bool _pressed; public event Action Clicked; public void SetTooltipLabel(string label) { _pendingLabel = label ?? string.Empty; if ((Object)(object)_hoverLabelText != (Object)null) { _hoverLabelText.text = _pendingLabel; ResizeLabelToContent(); } } public void OnPointerEnter(PointerEventData e) { _hovered = true; EnsureHoverLabel(); SetLabelVisible(visible: true); Refresh(); } public void OnPointerExit(PointerEventData e) { _hovered = false; _pressed = false; SetLabelVisible(visible: false); Refresh(); } public void OnPointerDown(PointerEventData e) { _pressed = true; Refresh(); } public void OnPointerUp(PointerEventData e) { _pressed = false; Refresh(); } public void OnPointerClick(PointerEventData e) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)e.button == 0) { this.Clicked?.Invoke(); } } private void Refresh() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Background == (Object)null)) { ((Graphic)Background).color = Color32.op_Implicit(_pressed ? PressedColor : (_hovered ? HoverColor : NormalColor)); } } private void EnsureHoverLabel() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Expected O, but got Unknown //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_hoverLabelRoot != (Object)null)) { _hoverLabelRoot = new GameObject("HoverLabel", new Type[2] { typeof(RectTransform), typeof(Image) }); _hoverLabelRoot.transform.SetParent(((Component)this).transform, false); RectTransform val = (RectTransform)_hoverLabelRoot.transform; val.anchorMin = new Vector2(0.5f, 0f); val.anchorMax = new Vector2(0.5f, 0f); val.pivot = new Vector2(0.5f, 1f); val.anchoredPosition = new Vector2(0f, -6f); val.sizeDelta = new Vector2(140f, 22f); Image component = _hoverLabelRoot.GetComponent(); component.sprite = UiStyle.SolidSprite; ((Graphic)component).color = Color32.op_Implicit(UiStyle.WoodBorder); ((Graphic)component).raycastTarget = false; GameObject val2 = new GameObject("Fill", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent(_hoverLabelRoot.transform, false); RectTransform val3 = (RectTransform)val2.transform; val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.offsetMin = new Vector2(2f, 2f); val3.offsetMax = new Vector2(-2f, -2f); Image component2 = val2.GetComponent(); component2.sprite = UiStyle.SolidSprite; ((Graphic)component2).color = Color32.op_Implicit(UiStyle.PanelFill); ((Graphic)component2).raycastTarget = false; GameObject val4 = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val4.transform.SetParent(_hoverLabelRoot.transform, false); _hoverLabelText = (TMP_Text)(object)val4.AddComponent(); _hoverLabelText.alignment = (TextAlignmentOptions)514; _hoverLabelText.fontSize = 12f; ((Graphic)_hoverLabelText).color = Color32.op_Implicit(UiStyle.HeaderGold); _hoverLabelText.enableWordWrapping = false; _hoverLabelText.overflowMode = (TextOverflowModes)0; ((Graphic)_hoverLabelText).raycastTarget = false; _hoverLabelText.richText = true; if ((Object)(object)UiStyle.Font != (Object)null) { _hoverLabelText.font = UiStyle.Font; } _hoverLabelText.text = _pendingLabel; RectTransform rectTransform = _hoverLabelText.rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = new Vector2(6f, 0f); rectTransform.offsetMax = new Vector2(-6f, 0f); ResizeLabelToContent(); _hoverLabelRoot.SetActive(false); } } private void ResizeLabelToContent() { //IL_0051: 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) if (!((Object)(object)_hoverLabelRoot == (Object)null) && !((Object)(object)_hoverLabelText == (Object)null)) { _hoverLabelText.ForceMeshUpdate(false, false); float num = Mathf.Max(80f, _hoverLabelText.preferredWidth + 16f); ((RectTransform)_hoverLabelRoot.transform).sizeDelta = new Vector2(num, 22f); } } private void SetLabelVisible(bool visible) { if (!((Object)(object)_hoverLabelRoot == (Object)null)) { _hoverLabelRoot.SetActive(visible); if (visible) { _hoverLabelRoot.transform.SetAsLastSibling(); } } } private void OnDisable() { _hovered = false; _pressed = false; SetLabelVisible(visible: false); Refresh(); } } internal sealed class DragHandle : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IDragHandler, IBeginDragHandler { public RectTransform Target; private Vector2 _grabOffset; public event Action Moved; public void OnPointerDown(PointerEventData e) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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) if (!((Object)(object)Target == (Object)null)) { Vector2 val = default(Vector2); RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)((Transform)Target).parent, e.position, e.pressEventCamera, ref val); _grabOffset = Target.anchoredPosition - val; } } public void OnBeginDrag(PointerEventData e) { OnPointerDown(e); } public void OnDrag(PointerEventData e) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_003b: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); if (!((Object)(object)Target == (Object)null) && RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)((Transform)Target).parent, e.position, e.pressEventCamera, ref val)) { Target.anchoredPosition = val + _grabOffset; this.Moved?.Invoke(Target.anchoredPosition.x, Target.anchoredPosition.y); } } } private readonly Transform _canvasRoot; private GameObject _panel; private RectTransform _panelRt; private TMP_Text _trackedLabel; private TMP_Text _valueLabel; private DragHandle _dragHandle; private Image _tooltipToggleIcon; private TooltipToggleButton _tooltipToggle; public RectTransform Rect => _panelRt; public bool IsVisible { get { if ((Object)(object)_panel != (Object)null) { return _panel.activeSelf; } return false; } } public event Action PlacementChanged; public event Action TooltipToggleClicked; public CropHudView(Transform canvasRoot) { _canvasRoot = canvasRoot; Build(); } public void SetVisible(bool visible) { if ((Object)(object)_panel == (Object)null) { Build(); } if ((Object)(object)_panel != (Object)null) { _panel.SetActive(visible); } } public void SetPlacement(float x, float y) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_panelRt == (Object)null) { Build(); } if ((Object)(object)_panelRt != (Object)null) { _panelRt.anchoredPosition = new Vector2(x, 0f - y); } } public void SetScale(float scale) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_panelRt == (Object)null) { Build(); } if ((Object)(object)_panelRt != (Object)null) { ((Transform)_panelRt).localScale = new Vector3(scale, scale, 1f); } } public void UpdateStats(int trackedCount, long projectedGold) { if (!((Object)(object)_trackedLabel == (Object)null)) { _trackedLabel.text = $"Tracked crops: {trackedCount}"; _valueLabel.text = $"Projected sell: {projectedGold:N0}g"; } } public void SetTooltipEnabled(bool enabled) { //IL_0038: 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_003d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_tooltipToggleIcon == (Object)null)) { _tooltipToggleIcon.sprite = UiStyle.GetIcon(enabled ? UiStyle.IconKind.Tooltip : UiStyle.IconKind.TooltipOff); ((Graphic)_tooltipToggleIcon).color = Color32.op_Implicit(enabled ? UiStyle.HeaderGold : UiStyle.TextSub); if ((Object)(object)_tooltipToggle != (Object)null) { _tooltipToggle.SetTooltipLabel(enabled ? "Toggle Crop Tooltips (on)" : "Toggle Crop Tooltips (off)"); } } } public void Destroy() { if ((Object)(object)_panel != (Object)null) { Object.Destroy((Object)(object)_panel); _panel = null; _panelRt = null; _trackedLabel = null; _valueLabel = null; _dragHandle = null; } } private void Build() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Expected O, but got Unknown //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_0407: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_0456: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_047f: Unknown result type (might be due to invalid IL or missing references) //IL_04ae: Unknown result type (might be due to invalid IL or missing references) //IL_04b4: Expected O, but got Unknown //IL_04cc: Unknown result type (might be due to invalid IL or missing references) //IL_04d1: Unknown result type (might be due to invalid IL or missing references) //IL_04dc: Unknown result type (might be due to invalid IL or missing references) //IL_04e6: Unknown result type (might be due to invalid IL or missing references) //IL_04f1: Unknown result type (might be due to invalid IL or missing references) //IL_04fb: Unknown result type (might be due to invalid IL or missing references) //IL_0506: Unknown result type (might be due to invalid IL or missing references) //IL_0510: Unknown result type (might be due to invalid IL or missing references) //IL_051b: Unknown result type (might be due to invalid IL or missing references) //IL_052f: Unknown result type (might be due to invalid IL or missing references) //IL_0550: Unknown result type (might be due to invalid IL or missing references) //IL_0555: Unknown result type (might be due to invalid IL or missing references) //IL_056f: Unknown result type (might be due to invalid IL or missing references) //IL_0599: Unknown result type (might be due to invalid IL or missing references) //IL_05ae: Unknown result type (might be due to invalid IL or missing references) //IL_05c3: Unknown result type (might be due to invalid IL or missing references) //IL_05cd: Unknown result type (might be due to invalid IL or missing references) //IL_0605: Unknown result type (might be due to invalid IL or missing references) //IL_060a: Unknown result type (might be due to invalid IL or missing references) //IL_0620: Unknown result type (might be due to invalid IL or missing references) //IL_0625: Unknown result type (might be due to invalid IL or missing references) //IL_063e: Unknown result type (might be due to invalid IL or missing references) //IL_0643: Unknown result type (might be due to invalid IL or missing references) //IL_0677: Unknown result type (might be due to invalid IL or missing references) //IL_067e: Expected O, but got Unknown //IL_0698: Unknown result type (might be due to invalid IL or missing references) //IL_069d: Unknown result type (might be due to invalid IL or missing references) //IL_06a8: Unknown result type (might be due to invalid IL or missing references) //IL_06b2: Unknown result type (might be due to invalid IL or missing references) //IL_06bd: Unknown result type (might be due to invalid IL or missing references) //IL_06c7: Unknown result type (might be due to invalid IL or missing references) //IL_06d2: Unknown result type (might be due to invalid IL or missing references) //IL_06e6: Unknown result type (might be due to invalid IL or missing references) //IL_06fa: Unknown result type (might be due to invalid IL or missing references) //IL_0719: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvasRoot == (Object)null)) { _panel = new GameObject("CropOptimizer_Hud", new Type[2] { typeof(RectTransform), typeof(Image) }); _panel.transform.SetParent(_canvasRoot, false); _panelRt = (RectTransform)_panel.transform; _panelRt.anchorMin = new Vector2(0f, 1f); _panelRt.anchorMax = new Vector2(0f, 1f); _panelRt.pivot = new Vector2(0f, 1f); _panelRt.anchoredPosition = new Vector2(24f, -80f); _panelRt.sizeDelta = new Vector2(320f, 96f); Image component = _panel.GetComponent(); component.sprite = UiStyle.SolidSprite; component.type = (Type)0; ((Graphic)component).color = Color32.op_Implicit(UiStyle.WoodBorder); ((Graphic)component).raycastTarget = true; GameObject val = new GameObject("Fill", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(_panel.transform, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = new Vector2(3f, 3f); val2.offsetMax = new Vector2(-3f, -3f); Image component2 = val.GetComponent(); component2.sprite = UiStyle.SolidSprite; ((Graphic)component2).color = Color32.op_Implicit(UiStyle.PanelFill); ((Graphic)component2).raycastTarget = false; GameObject val3 = new GameObject("Header", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(val.transform, false); RectTransform val4 = (RectTransform)val3.transform; val4.anchorMin = new Vector2(0f, 1f); val4.anchorMax = new Vector2(1f, 1f); val4.pivot = new Vector2(0.5f, 1f); val4.anchoredPosition = Vector2.zero; val4.sizeDelta = new Vector2(0f, 30f); Image component3 = val3.GetComponent(); component3.sprite = UiStyle.SolidSprite; ((Graphic)component3).color = Color32.op_Implicit(UiStyle.HeaderFill); _dragHandle = val3.AddComponent(); _dragHandle.Target = _panelRt; _dragHandle.Moved += delegate(float x, float y) { this.PlacementChanged?.Invoke(x, 0f - y); }; GameObject val5 = new GameObject("Divider", new Type[2] { typeof(RectTransform), typeof(Image) }); val5.transform.SetParent(val.transform, false); RectTransform val6 = (RectTransform)val5.transform; val6.anchorMin = new Vector2(0f, 1f); val6.anchorMax = new Vector2(1f, 1f); val6.pivot = new Vector2(0.5f, 1f); val6.anchoredPosition = new Vector2(0f, -30f); val6.sizeDelta = new Vector2(0f, 1f); Image component4 = val5.GetComponent(); component4.sprite = UiStyle.SolidSprite; ((Graphic)component4).color = Color32.op_Implicit(UiStyle.WoodBorderLit); ((Graphic)component4).raycastTarget = false; RectTransform rectTransform = ((Graphic)CreateIcon(val3.transform, UiStyle.IconKind.Sprout, UiStyle.Sprout, 20f)).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0.5f); rectTransform.anchorMax = new Vector2(0f, 0.5f); rectTransform.pivot = new Vector2(0f, 0.5f); rectTransform.anchoredPosition = new Vector2(10f, 0f); RectTransform rectTransform2 = CreateText(val3.transform, "Crop Optimizer", 16f, (FontStyles)1, UiStyle.HeaderGold, (TextAlignmentOptions)4097).rectTransform; rectTransform2.anchorMin = new Vector2(0f, 0f); rectTransform2.anchorMax = new Vector2(1f, 1f); rectTransform2.offsetMin = new Vector2(36f, 0f); rectTransform2.offsetMax = new Vector2(-36f, 0f); GameObject val7 = new GameObject("TooltipToggle", new Type[2] { typeof(RectTransform), typeof(Image) }); val7.transform.SetParent(val3.transform, false); RectTransform val8 = (RectTransform)val7.transform; val8.anchorMin = new Vector2(1f, 0.5f); val8.anchorMax = new Vector2(1f, 0.5f); val8.pivot = new Vector2(1f, 0.5f); val8.anchoredPosition = new Vector2(-8f, 0f); val8.sizeDelta = new Vector2(24f, 24f); Image component5 = val7.GetComponent(); component5.sprite = UiStyle.SolidSprite; ((Graphic)component5).color = Color32.op_Implicit(new Color32((byte)0, (byte)0, (byte)0, (byte)0)); ((Graphic)component5).raycastTarget = true; _tooltipToggleIcon = CreateIcon(val7.transform, UiStyle.IconKind.Tooltip, UiStyle.HeaderGold, 16f); RectTransform rectTransform3 = ((Graphic)_tooltipToggleIcon).rectTransform; rectTransform3.anchorMin = new Vector2(0.5f, 0.5f); rectTransform3.anchorMax = new Vector2(0.5f, 0.5f); rectTransform3.pivot = new Vector2(0.5f, 0.5f); rectTransform3.anchoredPosition = Vector2.zero; ((Graphic)_tooltipToggleIcon).raycastTarget = false; _tooltipToggle = val7.AddComponent(); _tooltipToggle.Background = component5; _tooltipToggle.NormalColor = new Color32((byte)0, (byte)0, (byte)0, (byte)0); _tooltipToggle.HoverColor = new Color32((byte)176, (byte)122, (byte)62, (byte)96); _tooltipToggle.PressedColor = new Color32((byte)176, (byte)122, (byte)62, (byte)160); _tooltipToggle.Clicked += delegate { this.TooltipToggleClicked?.Invoke(); }; GameObject val9 = new GameObject("Body", new Type[1] { typeof(RectTransform) }); val9.transform.SetParent(val.transform, false); RectTransform val10 = (RectTransform)val9.transform; val10.anchorMin = new Vector2(0f, 0f); val10.anchorMax = new Vector2(1f, 1f); val10.offsetMin = new Vector2(14f, 8f); val10.offsetMax = new Vector2(-14f, -36f); _trackedLabel = AddRow(val9.transform, UiStyle.IconKind.Sprout, UiStyle.Sprout, "Tracked crops: 0", 0); _valueLabel = AddRow(val9.transform, UiStyle.IconKind.Coin, UiStyle.Coin, "Projected sell: 0g", 1); } } private TMP_Text AddRow(Transform parent, UiStyle.IconKind icon, Color32 iconColor, string initialText, int rowIndex) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject($"Row_{icon}", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(1f, 1f); val2.pivot = new Vector2(0f, 1f); val2.anchoredPosition = new Vector2(0f, (float)(-rowIndex) * 24f); val2.sizeDelta = new Vector2(0f, 22f); RectTransform rectTransform = ((Graphic)CreateIcon(val.transform, icon, iconColor, 18f)).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0.5f); rectTransform.anchorMax = new Vector2(0f, 0.5f); rectTransform.pivot = new Vector2(0f, 0.5f); rectTransform.anchoredPosition = new Vector2(0f, 0f); TMP_Text obj = CreateText(val.transform, initialText, 15f, (FontStyles)0, UiStyle.TextDark, (TextAlignmentOptions)4097); RectTransform rectTransform2 = obj.rectTransform; rectTransform2.anchorMin = new Vector2(0f, 0f); rectTransform2.anchorMax = new Vector2(1f, 1f); rectTransform2.offsetMin = new Vector2(26f, 0f); rectTransform2.offsetMax = new Vector2(-2f, 0f); return obj; } private static Image CreateIcon(Transform parent, UiStyle.IconKind kind, Color32 color, float size) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0048: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject($"Icon_{kind}", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); ((RectTransform)val.transform).sizeDelta = new Vector2(size, size); Image component = val.GetComponent(); component.sprite = UiStyle.GetIcon(kind); ((Graphic)component).color = Color32.op_Implicit(color); ((Graphic)component).raycastTarget = false; return component; } private static TMP_Text CreateText(Transform parent, string text, float size, FontStyles style, Color32 color, TextAlignmentOptions align) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); TextMeshProUGUI val2 = val.AddComponent(); ((TMP_Text)val2).text = text; ((TMP_Text)val2).fontSize = size; ((TMP_Text)val2).fontStyle = style; ((Graphic)val2).color = Color32.op_Implicit(color); ((TMP_Text)val2).alignment = align; ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)1; ((Graphic)val2).raycastTarget = false; ((TMP_Text)val2).richText = true; if ((Object)(object)UiStyle.Font != (Object)null) { ((TMP_Text)val2).font = UiStyle.Font; } return (TMP_Text)(object)val2; } } internal static class CropTileReflection { private static bool _initialized; private static Type _tileManagerType; private static object _tileManagerInstance; private static MethodInfo _isWateredMethod; private static MethodInfo _isHoedMethod; private static MethodInfo _isHoedOrWateredMethod; private static FieldInfo _farmingDataField; private static FieldInfo _farmingTileMapField; private static FieldInfo _waterTileMapField; private static FieldInfo _wateredTilesField; private static MethodInfo _worldToCellMethod; private static MethodInfo _waterWorldToCellMethod; private static MethodInfo _waterHasTileMethod; private static readonly Vector2Int[] SearchOffsets = (Vector2Int[])(object)new Vector2Int[8] { new Vector2Int(-1, 0), new Vector2Int(1, 0), new Vector2Int(0, -1), new Vector2Int(0, 1), new Vector2Int(-1, -1), new Vector2Int(-1, 1), new Vector2Int(1, -1), new Vector2Int(1, 1) }; public static void EnsureInitialized() { if (_initialized) { return; } _initialized = true; try { _tileManagerType = ReflectionHelper.FindWishType("TileManager"); if (!(_tileManagerType == null)) { _isWateredMethod = _tileManagerType.GetMethod("IsWatered", new Type[1] { typeof(Vector2Int) }); _isHoedMethod = _tileManagerType.GetMethod("IsHoed", new Type[1] { typeof(Vector2Int) }); _isHoedOrWateredMethod = _tileManagerType.GetMethod("IsHoedOrWatered", new Type[1] { typeof(Vector2Int) }); _farmingDataField = _tileManagerType.GetField("farmingData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); _farmingTileMapField = _tileManagerType.GetField("farmingTileMap", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); _waterTileMapField = _tileManagerType.GetField("waterTileMap", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); _wateredTilesField = _tileManagerType.GetField("wateredTiles", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); } } catch { } } private static object ResolveInstance() { if (_tileManagerType == null) { return null; } object tileManagerInstance = _tileManagerInstance; Object val = (Object)((tileManagerInstance is Object) ? tileManagerInstance : null); if (val != null && val == (Object)null) { _tileManagerInstance = null; } if (_tileManagerInstance == null) { try { _tileManagerInstance = ReflectionHelper.GetSingletonInstance(_tileManagerType); } catch { } } if (_tileManagerInstance != null && _worldToCellMethod == null && _farmingTileMapField != null) { try { object value = _farmingTileMapField.GetValue(_tileManagerInstance); if (value != null) { _worldToCellMethod = value.GetType().GetMethod("WorldToCell", new Type[1] { typeof(Vector3) }); } } catch { } } return _tileManagerInstance; } public static bool TryGetTileCoordForCrop(Component crop, out Vector2Int tile) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) tile = default(Vector2Int); if ((Object)(object)crop == (Object)null) { return false; } EnsureInitialized(); object obj = ResolveInstance(); Vector3 position = crop.transform.position; if (CropGrowthPatch.TryGetCropGridPosition(crop, out var tile2)) { tile = tile2; return true; } if (obj != null) { try { if (_worldToCellMethod != null && _farmingTileMapField != null) { object value = _farmingTileMapField.GetValue(obj); if (value != null) { object obj2 = _worldToCellMethod.Invoke(value, new object[1] { position }); if (obj2 is Vector3Int) { Vector3Int val = (Vector3Int)obj2; tile = new Vector2Int(((Vector3Int)(ref val)).x, ((Vector3Int)(ref val)).y); return true; } } } } catch { } } if (obj != null && _farmingDataField != null && TryFindNearestFarmingKey(obj, position, out var tile3, 4096f)) { tile = tile3; return true; } tile = new Vector2Int(Mathf.RoundToInt(position.x - 0.5f), Mathf.RoundToInt(position.y - 0.5f)); return true; } private static bool TryFindNearestFarmingKey(object tm, Vector3 worldPos, out Vector2Int tile, float maxSqDist) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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) tile = default(Vector2Int); try { if (!(_farmingDataField.GetValue(tm) is IDictionary dictionary) || dictionary.Count == 0) { return false; } float num = maxSqDist; bool result = false; foreach (object key in dictionary.Keys) { if (key is Vector2Int val) { float num2 = (float)((Vector2Int)(ref val)).x - worldPos.x; float num3 = (float)((Vector2Int)(ref val)).y - worldPos.y; float num4 = num2 * num2 + num3 * num3; if (num4 < num) { num = num4; tile = val; result = true; } } } return result; } catch { return false; } } public static string DescribeFarmingTileState(Vector2Int tile) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) Vector2Int matchedTile; return DescribeFarmingTileState(tile, Vector3.zero, haveWorldPos: false, out matchedTile); } public static string DescribeFarmingTileState(Vector2Int tile, out Vector2Int matchedTile) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return DescribeFarmingTileState(tile, Vector3.zero, haveWorldPos: false, out matchedTile); } public static string DescribeFarmingTileState(Vector2Int tile, Vector3 worldPosForWaterMap, bool haveWorldPos, out Vector2Int matchedTile) { //IL_0001: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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) matchedTile = tile; EnsureInitialized(); object obj = ResolveInstance(); if (haveWorldPos && obj != null && TryWaterTileMapHasTile(obj, worldPosForWaterMap)) { return "watered"; } if (obj == null) { return null; } try { string text = ProbeTile(obj, tile); if (text != null) { matchedTile = tile; return text; } Vector2Int[] searchOffsets = SearchOffsets; Vector2Int val2 = default(Vector2Int); for (int i = 0; i < searchOffsets.Length; i++) { Vector2Int val = searchOffsets[i]; ((Vector2Int)(ref val2))..ctor(((Vector2Int)(ref tile)).x + ((Vector2Int)(ref val)).x, ((Vector2Int)(ref tile)).y + ((Vector2Int)(ref val)).y); text = ProbeTile(obj, val2); if (text != null) { matchedTile = val2; return text; } } } catch { } return null; } private static bool TryWaterTileMapHasTile(object tm, Vector3 worldPos) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) try { if (_waterTileMapField == null) { return false; } object value = _waterTileMapField.GetValue(tm); if (value == null) { return false; } if (_waterWorldToCellMethod == null) { _waterWorldToCellMethod = value.GetType().GetMethod("WorldToCell", new Type[1] { typeof(Vector3) }); } if (_waterHasTileMethod == null) { _waterHasTileMethod = value.GetType().GetMethod("HasTile", new Type[1] { typeof(Vector3Int) }); } if (_waterWorldToCellMethod == null || _waterHasTileMethod == null) { return false; } if (!(_waterWorldToCellMethod.Invoke(value, new object[1] { worldPos }) is Vector3Int val)) { return false; } object obj = _waterHasTileMethod.Invoke(value, new object[1] { val }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static int NextOffset(int current) { return current switch { 0 => 1, -1 => 0, _ => 2, }; } private static string ProbeTile(object tm, Vector2Int tile) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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) bool flag = false; bool flag2 = false; if (_isWateredMethod != null) { try { flag = (bool)_isWateredMethod.Invoke(tm, new object[1] { tile }); } catch { } } if (_isHoedMethod != null) { try { flag2 = (bool)_isHoedMethod.Invoke(tm, new object[1] { tile }); } catch { } } if (!flag) { try { flag = ScanWaterMembersForTile(tm, tile); } catch { } } if (flag) { return "watered"; } if (flag2) { return "hoed (dry)"; } if (_isHoedOrWateredMethod != null) { try { if ((bool)_isHoedOrWateredMethod.Invoke(tm, new object[1] { tile })) { return "hoed or watered"; } } catch { } } if (_farmingDataField != null) { try { if (_farmingDataField.GetValue(tm) is IDictionary dictionary && dictionary.Contains(tile)) { object obj5 = dictionary[tile]; if (obj5 != null) { string text = obj5.ToString(); if (text.IndexOf("Water", StringComparison.OrdinalIgnoreCase) >= 0 || text == "3") { return "watered"; } if (text.Equals("Hoed", StringComparison.OrdinalIgnoreCase) || text == "2") { return "hoed (dry)"; } return text; } } } catch { } } return null; } private static bool ScanWaterMembersForTile(object tm, Vector2Int tile) { //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_00a2: 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_00a6: Unknown result type (might be due to invalid IL or missing references) if (tm == null || _tileManagerType == null) { return false; } FieldInfo[] fields = _tileManagerType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.Name.IndexOf("water", StringComparison.OrdinalIgnoreCase) < 0) { continue; } object value; try { value = fieldInfo.GetValue(tm); } catch { continue; } if (value == null) { continue; } if (value is IDictionary dictionary && dictionary.Contains(tile)) { return true; } if (!(value is ICollection collection)) { continue; } foreach (object item in collection) { if (item is Vector2Int val && val == tile) { return true; } } } return false; } public static string BuildDebugSnapshot(Component crop, Vector2Int tile) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); Vector3? obj; if (crop == null) { obj = null; } else { Transform transform = crop.transform; obj = ((transform != null) ? new Vector3?(transform.position) : null); } stringBuilder.Append($"[HoverDebug] Tile probe for crop at {obj} → tile={tile}"); object obj2 = ResolveInstance(); stringBuilder.Append(", tm=" + ((obj2 != null) ? obj2.GetType().Name : "null")); stringBuilder.Append(", IsWatered(tile)=" + InvokeBoolSafe(_isWateredMethod, obj2, tile)); stringBuilder.Append(", IsHoed(tile)=" + InvokeBoolSafe(_isHoedMethod, obj2, tile)); stringBuilder.Append(", IsHoedOrWatered(tile)=" + InvokeBoolSafe(_isHoedOrWateredMethod, obj2, tile)); if (obj2 != null && (Object)(object)crop != (Object)null) { stringBuilder.Append(", waterTileMap.HasTile(worldPos)=" + (TryWaterTileMapHasTile(obj2, crop.transform.position) ? "true" : "false")); } try { if (obj2 != null && _farmingDataField != null) { IDictionary dictionary = _farmingDataField.GetValue(obj2) as IDictionary; stringBuilder.Append($", farmingData.count={dictionary?.Count ?? (-1)}"); if (dictionary != null) { object obj3 = (dictionary.Contains(tile) ? dictionary[tile] : null); stringBuilder.Append(", farmingData[tile]=" + ((obj3 == null) ? "" : obj3.ToString())); } } } catch (Exception ex) { stringBuilder.Append(", farmingDataErr=" + ex.Message); } try { if (obj2 != null) { stringBuilder.Append(", waterMembers=["); bool flag = false; FieldInfo[] fields = _tileManagerType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); foreach (FieldInfo fieldInfo in fields) { string name = fieldInfo.Name; if (!ContainsAny(name, "water", "Watered", "Watering")) { continue; } if (flag) { stringBuilder.Append(','); } flag = true; stringBuilder.Append(fieldInfo.FieldType.Name + " " + name); try { object? value = fieldInfo.GetValue(obj2); if (value is ICollection collection) { stringBuilder.Append($"(count={collection.Count})"); } if (value is IDictionary dictionary2 && dictionary2.Contains(tile)) { stringBuilder.Append($"(contains tile, val={dictionary2[tile]})"); } } catch { } } PropertyInfo[] properties = _tileManagerType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); foreach (PropertyInfo propertyInfo in properties) { string name2 = propertyInfo.Name; if (ContainsAny(name2, "water", "Watered", "Watering")) { if (flag) { stringBuilder.Append(','); } flag = true; stringBuilder.Append(propertyInfo.PropertyType.Name + " " + name2 + "(prop)"); } } MethodInfo[] methods = _tileManagerType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); foreach (MethodInfo methodInfo in methods) { string name3 = methodInfo.Name; if (!ContainsAny(name3, "water", "Watered", "Watering") || methodInfo.IsSpecialName) { continue; } if (flag) { stringBuilder.Append(','); } flag = true; ParameterInfo[] parameters = methodInfo.GetParameters(); stringBuilder.Append(methodInfo.ReturnType.Name + " " + name3 + "("); for (int j = 0; j < parameters.Length; j++) { if (j > 0) { stringBuilder.Append(','); } stringBuilder.Append(parameters[j].ParameterType.Name); } stringBuilder.Append(")"); } stringBuilder.Append(']'); } } catch (Exception ex2) { stringBuilder.Append(", waterScanErr=" + ex2.Message); } return stringBuilder.ToString(); } private static string InvokeBoolSafe(MethodInfo method, object tm, Vector2Int tile) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (method == null) { return ""; } if (tm == null) { return ""; } try { object obj = method.Invoke(tm, new object[1] { tile }); return (!(obj is bool)) ? $"<{obj}>" : (((bool)obj) ? "true" : "false"); } catch (Exception ex) { return ""; } } private static bool ContainsAny(string s, params string[] needles) { if (string.IsNullOrEmpty(s)) { return false; } foreach (string value in needles) { if (s.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } } internal sealed class CropTooltipView { private sealed class Row { public GameObject Root; public Image Icon; public TMP_Text Text; } private const float PanelWidth = 320f; private const float BorderWidth = 3f; private const float RowHeight = 24f; private const float HeaderHeight = 32f; private const float StripeHeight = 3f; private const float PaddingTop = 0f; private const float PaddingSide = 14f; private const float PaddingBottom = 10f; private const float HeaderToStripeGap = 0f; private const float StripeToBodyGap = 8f; private readonly Transform _canvasRoot; private GameObject _panel; private RectTransform _panelRt; private Image _headerIcon; private TMP_Text _headerTitle; private TMP_Text _headerSubtitle; private Image _qualityStripe; private readonly List _rows = new List(); private TMP_Text _extras; public bool IsVisible { get { if ((Object)(object)_panel != (Object)null) { return _panel.activeSelf; } return false; } } public RectTransform Rect => _panelRt; public CropTooltipView(Transform canvasRoot) { _canvasRoot = canvasRoot; Build(); } public void SetVisible(bool visible) { if ((Object)(object)_panel == (Object)null) { Build(); } if ((Object)(object)_panel != (Object)null) { _panel.SetActive(visible); } } public void Destroy() { if ((Object)(object)_panel != (Object)null) { Object.Destroy((Object)(object)_panel); _panel = null; _panelRt = null; _rows.Clear(); } } public void SetScreenPosition(Vector2 screenPoint) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_panelRt == (Object)null) { return; } RectTransform val = (RectTransform)((Transform)_panelRt).parent; Vector2 val2 = default(Vector2); if (RectTransformUtility.ScreenPointToLocalPointInRectangle(val, screenPoint, (Camera)null, ref val2)) { Rect rect = val.rect; Rect rect2 = _panelRt.rect; float width = ((Rect)(ref rect2)).width; rect2 = _panelRt.rect; float height = ((Rect)(ref rect2)).height; float num = val2.x - ((Rect)(ref rect)).xMin; float num2 = val2.y - ((Rect)(ref rect)).yMax; float num3 = num + 18f; if (num3 + width > ((Rect)(ref rect)).width) { num3 = num - 18f - width; } float num4 = num2 - 18f; if (num4 - height < 0f - ((Rect)(ref rect)).height) { num4 = num2 + 18f + height; } num3 = Mathf.Clamp(num3, 0f, Mathf.Max(0f, ((Rect)(ref rect)).width - width)); num4 = Mathf.Clamp(num4, Mathf.Min(0f, height - ((Rect)(ref rect)).height), 0f); _panelRt.anchoredPosition = new Vector2(num3, num4); } } public void SetScale(float scale) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_panelRt == (Object)null) { Build(); } if ((Object)(object)_panelRt != (Object)null) { ((Transform)_panelRt).localScale = new Vector3(scale, scale, 1f); } } public void ApplyContent(TooltipContent c) { //IL_0082: 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_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_panel == (Object)null) { Build(); } _headerTitle.text = (string.IsNullOrEmpty(c.Title) ? "Crop" : c.Title); if (!string.IsNullOrEmpty(c.HeaderTag)) { ((Component)_headerSubtitle).gameObject.SetActive(true); _headerSubtitle.text = c.HeaderTag; } else { ((Component)_headerSubtitle).gameObject.SetActive(false); } ((Graphic)_qualityStripe).color = Color32.op_Implicit(c.QualityColor); foreach (Row row2 in _rows) { row2.Root.SetActive(false); } int num = 0; foreach (RowSpec row3 in c.Rows) { if (num >= _rows.Count) { break; } if (row3 != null) { Row row = _rows[num++]; row.Root.SetActive(true); row.Icon.sprite = UiStyle.GetIcon(row3.Icon); ((Graphic)row.Icon).color = Color32.op_Implicit(row3.IconColor); row.Text.text = row3.Text; ((Graphic)row.Text).color = Color32.op_Implicit((row3.TextColor.a == 0) ? UiStyle.TextDark : row3.TextColor); float num2 = 0f - (43f + (float)(num - 1) * 24f); ((RectTransform)row.Root.transform).anchoredPosition = new Vector2(14f, num2); } } float num3 = 286f; float num4 = 0f; if (!string.IsNullOrWhiteSpace(c.Extras)) { ((Component)_extras).gameObject.SetActive(true); _extras.text = c.Extras; _extras.ForceMeshUpdate(false, false); RectTransform rectTransform = _extras.rectTransform; rectTransform.sizeDelta = new Vector2(num3, 0f); _extras.ForceMeshUpdate(false, false); num4 = Mathf.Max(14f, _extras.preferredHeight) + 4f; float num5 = 0f - (43f + (float)num * 24f + 4f); rectTransform.anchoredPosition = new Vector2(14f, num5); rectTransform.sizeDelta = new Vector2(num3, num4); } else { ((Component)_extras).gameObject.SetActive(false); } float num6 = 43f + (float)num * 24f + num4 + 10f + 6f; _panelRt.sizeDelta = new Vector2(320f, num6); } private void Build() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) //IL_03eb: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_0458: Unknown result type (might be due to invalid IL or missing references) //IL_045e: Expected O, but got Unknown //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_047b: Unknown result type (might be due to invalid IL or missing references) //IL_0486: Unknown result type (might be due to invalid IL or missing references) //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_049b: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04b0: Unknown result type (might be due to invalid IL or missing references) //IL_04ba: Unknown result type (might be due to invalid IL or missing references) //IL_04c5: Unknown result type (might be due to invalid IL or missing references) //IL_04d9: Unknown result type (might be due to invalid IL or missing references) //IL_0505: Unknown result type (might be due to invalid IL or missing references) //IL_050a: Unknown result type (might be due to invalid IL or missing references) //IL_0556: Unknown result type (might be due to invalid IL or missing references) //IL_0598: Unknown result type (might be due to invalid IL or missing references) //IL_05ad: Unknown result type (might be due to invalid IL or missing references) //IL_05c2: Unknown result type (might be due to invalid IL or missing references) //IL_05d6: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvasRoot == (Object)null)) { _panel = new GameObject("CropOptimizer_Tooltip", new Type[2] { typeof(RectTransform), typeof(Image) }); _panel.transform.SetParent(_canvasRoot, false); _panelRt = (RectTransform)_panel.transform; _panelRt.anchorMin = new Vector2(0f, 1f); _panelRt.anchorMax = new Vector2(0f, 1f); _panelRt.pivot = new Vector2(0f, 1f); _panelRt.sizeDelta = new Vector2(320f, 120f); Image component = _panel.GetComponent(); component.sprite = UiStyle.SolidSprite; component.type = (Type)0; ((Graphic)component).color = Color32.op_Implicit(UiStyle.WoodBorder); ((Graphic)component).raycastTarget = false; GameObject val = new GameObject("Fill", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(_panel.transform, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = new Vector2(3f, 3f); val2.offsetMax = new Vector2(-3f, -3f); Image component2 = val.GetComponent(); component2.sprite = UiStyle.SolidSprite; ((Graphic)component2).color = Color32.op_Implicit(UiStyle.PanelFill); ((Graphic)component2).raycastTarget = false; GameObject val3 = new GameObject("Header", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(val.transform, false); RectTransform val4 = (RectTransform)val3.transform; val4.anchorMin = new Vector2(0f, 1f); val4.anchorMax = new Vector2(1f, 1f); val4.pivot = new Vector2(0.5f, 1f); val4.anchoredPosition = Vector2.zero; val4.sizeDelta = new Vector2(0f, 32f); Image component3 = val3.GetComponent(); component3.sprite = UiStyle.SolidSprite; ((Graphic)component3).color = Color32.op_Implicit(UiStyle.HeaderFill); ((Graphic)component3).raycastTarget = false; _headerIcon = CreateIcon(val3.transform, UiStyle.IconKind.Sprout, UiStyle.Sprout, 22f); RectTransform rectTransform = ((Graphic)_headerIcon).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0.5f); rectTransform.anchorMax = new Vector2(0f, 0.5f); rectTransform.pivot = new Vector2(0f, 0.5f); rectTransform.anchoredPosition = new Vector2(10f, 0f); _headerTitle = CreateText(val3.transform, "Crop", 17f, (FontStyles)1, UiStyle.HeaderGold, (TextAlignmentOptions)4097); RectTransform rectTransform2 = _headerTitle.rectTransform; rectTransform2.anchorMin = new Vector2(0f, 0f); rectTransform2.anchorMax = new Vector2(1f, 1f); rectTransform2.offsetMin = new Vector2(38f, 0f); rectTransform2.offsetMax = new Vector2(-90f, 0f); _headerSubtitle = CreateText(val3.transform, "", 11f, (FontStyles)2, UiStyle.ParchmentLight, (TextAlignmentOptions)4100); RectTransform rectTransform3 = _headerSubtitle.rectTransform; rectTransform3.anchorMin = new Vector2(1f, 0f); rectTransform3.anchorMax = new Vector2(1f, 1f); rectTransform3.pivot = new Vector2(1f, 0.5f); rectTransform3.offsetMin = new Vector2(-90f, 0f); rectTransform3.offsetMax = new Vector2(-10f, 0f); rectTransform3.sizeDelta = new Vector2(80f, 0f); GameObject val5 = new GameObject("QualityStripe", new Type[2] { typeof(RectTransform), typeof(Image) }); val5.transform.SetParent(val.transform, false); RectTransform val6 = (RectTransform)val5.transform; val6.anchorMin = new Vector2(0f, 1f); val6.anchorMax = new Vector2(1f, 1f); val6.pivot = new Vector2(0.5f, 1f); val6.anchoredPosition = new Vector2(0f, -32f); val6.sizeDelta = new Vector2(0f, 3f); _qualityStripe = val5.GetComponent(); _qualityStripe.sprite = UiStyle.SolidSprite; ((Graphic)_qualityStripe).color = Color32.op_Implicit(UiStyle.QualityNormal); ((Graphic)_qualityStripe).raycastTarget = false; for (int i = 0; i < 8; i++) { _rows.Add(BuildRow(val.transform, i)); } _extras = CreateText(val.transform, "", 11f, (FontStyles)2, UiStyle.TextSub, (TextAlignmentOptions)257); _extras.enableWordWrapping = true; _extras.overflowMode = (TextOverflowModes)0; RectTransform rectTransform4 = _extras.rectTransform; rectTransform4.anchorMin = new Vector2(0f, 1f); rectTransform4.anchorMax = new Vector2(0f, 1f); rectTransform4.pivot = new Vector2(0f, 1f); rectTransform4.sizeDelta = new Vector2(286f, 0f); ((Component)_extras).gameObject.SetActive(false); } } private Row BuildRow(Transform parent, int _) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //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) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Row", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(0f, 1f); val2.pivot = new Vector2(0f, 1f); val2.sizeDelta = new Vector2(286f, 24f); Image val3 = CreateIcon(val.transform, UiStyle.IconKind.Sprout, UiStyle.Sprout, 18f); RectTransform rectTransform = ((Graphic)val3).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0.5f); rectTransform.anchorMax = new Vector2(0f, 0.5f); rectTransform.pivot = new Vector2(0f, 0.5f); TMP_Text val4 = CreateText(val.transform, "", 15f, (FontStyles)0, UiStyle.TextDark, (TextAlignmentOptions)4097); RectTransform rectTransform2 = val4.rectTransform; rectTransform2.anchorMin = new Vector2(0f, 0f); rectTransform2.anchorMax = new Vector2(1f, 1f); rectTransform2.offsetMin = new Vector2(26f, 0f); rectTransform2.offsetMax = new Vector2(-2f, 0f); val.SetActive(false); return new Row { Root = val, Icon = val3, Text = val4 }; } private static Image CreateIcon(Transform parent, UiStyle.IconKind kind, Color32 color, float size) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0048: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject($"Icon_{kind}", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); ((RectTransform)val.transform).sizeDelta = new Vector2(size, size); Image component = val.GetComponent(); component.sprite = UiStyle.GetIcon(kind); ((Graphic)component).color = Color32.op_Implicit(color); ((Graphic)component).raycastTarget = false; return component; } private static TMP_Text CreateText(Transform parent, string text, float size, FontStyles style, Color32 color, TextAlignmentOptions align) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); TextMeshProUGUI val2 = val.AddComponent(); ((TMP_Text)val2).text = text; ((TMP_Text)val2).fontSize = size; ((TMP_Text)val2).fontStyle = style; ((Graphic)val2).color = Color32.op_Implicit(color); ((TMP_Text)val2).alignment = align; ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).overflowMode = (TextOverflowModes)1; ((Graphic)val2).raycastTarget = false; ((TMP_Text)val2).richText = true; if ((Object)(object)UiStyle.Font != (Object)null) { ((TMP_Text)val2).font = UiStyle.Font; } return (TMP_Text)(object)val2; } } internal sealed class TooltipContent { public string Title; public string HeaderTag; public Color32 QualityColor = UiStyle.QualityNormal; public List Rows = new List(); public string Extras; } internal sealed class RowSpec { public UiStyle.IconKind Icon; public Color32 IconColor; public string Text; public Color32 TextColor; public static RowSpec Make(UiStyle.IconKind icon, Color32 iconColor, string text, Color32 textColor = default(Color32)) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) return new RowSpec { Icon = icon, IconColor = iconColor, Text = text, TextColor = textColor }; } } internal static class UiStyle { public enum IconKind { Sprout, Water, Fertilizer, Mana, Coin, Clock, Tile, Quality, Ready, Tooltip, TooltipOff } public static readonly Color32 WoodBorder = new Color32((byte)107, (byte)66, (byte)34, byte.MaxValue); public static readonly Color32 WoodBorderLit = new Color32((byte)176, (byte)122, (byte)62, byte.MaxValue); public static readonly Color32 PanelFill = new Color32((byte)36, (byte)26, (byte)18, (byte)242); public static readonly Color32 HeaderFill = new Color32((byte)58, (byte)40, (byte)26, byte.MaxValue); public static readonly Color32 Divider = new Color32((byte)107, (byte)66, (byte)34, (byte)128); public static readonly Color32 HeaderGold = new Color32((byte)247, (byte)217, (byte)130, byte.MaxValue); public static readonly Color32 TextDark = new Color32((byte)240, (byte)230, (byte)204, byte.MaxValue); public static readonly Color32 TextSub = new Color32((byte)184, (byte)160, (byte)120, byte.MaxValue); public static readonly Color32 Shadow = new Color32((byte)0, (byte)0, (byte)0, (byte)176); public static readonly Color32 ParchmentLight = new Color32((byte)234, (byte)213, (byte)154, byte.MaxValue); public static readonly Color32 ParchmentDeep = new Color32((byte)201, (byte)174, (byte)117, byte.MaxValue); public static readonly Color32 QualityNormal = new Color32((byte)207, (byte)190, (byte)142, byte.MaxValue); public static readonly Color32 QualitySilver = new Color32((byte)204, (byte)211, (byte)216, byte.MaxValue); public static readonly Color32 QualityGold = new Color32((byte)232, (byte)195, (byte)80, byte.MaxValue); public static readonly Color32 Water = new Color32((byte)79, (byte)176, (byte)224, byte.MaxValue); public static readonly Color32 Fertilizer = new Color32((byte)124, (byte)179, (byte)66, byte.MaxValue); public static readonly Color32 Mana = new Color32((byte)181, (byte)106, (byte)224, byte.MaxValue); public static readonly Color32 Coin = new Color32(byte.MaxValue, (byte)194, (byte)60, byte.MaxValue); public static readonly Color32 Clock = new Color32((byte)211, (byte)90, (byte)45, byte.MaxValue); public static readonly Color32 Tile = new Color32((byte)139, (byte)106, (byte)62, byte.MaxValue); public static readonly Color32 Sprout = new Color32((byte)110, (byte)184, (byte)74, byte.MaxValue); private static Sprite _panelSprite; private static Sprite _headerSprite; private static Sprite _stripeSprite; private static readonly Dictionary IconCache = new Dictionary(); private static TMP_FontAsset _font; public static Sprite PanelSprite => _panelSprite ?? (_panelSprite = BuildPanelSprite()); public static Sprite HeaderSprite => _headerSprite ?? (_headerSprite = BuildHeaderSprite()); public static Sprite SolidSprite => _stripeSprite ?? (_stripeSprite = BuildSolidSprite()); public static TMP_FontAsset Font { get { if ((Object)(object)_font != (Object)null) { return _font; } try { _font = TMP_Settings.defaultFontAsset; if ((Object)(object)_font == (Object)null) { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); if (array != null && array.Length != 0) { _font = array[0]; } } } catch { } return _font; } } private static Sprite BuildPanelSprite() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BuildSolidColoredSprite(Color.white); } private static Sprite BuildHeaderSprite() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BuildSolidColoredSprite(Color.white); } private static Sprite BuildSolidColoredSprite(Color c) { //IL_0010: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) Texture2D val = NewTexture(4, 4); Color32[] array = (Color32[])(object)new Color32[16]; Color32 val2 = Color32.op_Implicit(c); for (int i = 0; i < array.Length; i++) { array[i] = val2; } val.SetPixels32(array); val.Apply(false, false); return Sprite.Create(val, new Rect(0f, 0f, 4f, 4f), new Vector2(0.5f, 0.5f)); } private static Sprite BuildSolidSprite() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BuildSolidColoredSprite(Color.white); } public static Sprite GetIcon(IconKind kind) { string key = kind.ToString(); if (IconCache.TryGetValue(key, out Sprite value) && (Object)(object)value != (Object)null) { return value; } value = BuildIcon(kind); IconCache[key] = value; return value; } private static Sprite BuildIcon(IconKind kind) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) Color32[] array = (Color32[])(object)new Color32[256]; Color32 fg = Color32.op_Implicit(Color.white); Color32 outline = default(Color32); ((Color32)(ref outline))..ctor((byte)0, (byte)0, (byte)0, (byte)192); switch (kind) { case IconKind.Sprout: DrawSprout(array, 16, fg, outline); break; case IconKind.Water: DrawWaterDrop(array, 16, fg, outline); break; case IconKind.Fertilizer: DrawLeaf(array, 16, fg, outline); break; case IconKind.Mana: DrawStar(array, 16, fg, outline); break; case IconKind.Coin: DrawCoin(array, 16, fg, outline); break; case IconKind.Clock: DrawClock(array, 16, fg, outline); break; case IconKind.Tile: DrawTile(array, 16, fg, outline); break; case IconKind.Quality: DrawDiamond(array, 16, fg, outline); break; case IconKind.Ready: DrawCheck(array, 16, fg, outline); break; case IconKind.Tooltip: DrawTooltip(array, 16, fg, outline, strike: false); break; case IconKind.TooltipOff: DrawTooltip(array, 16, fg, outline, strike: true); break; } Texture2D obj = NewTexture(16, 16); obj.SetPixels32(array); obj.Apply(false, false); return Sprite.Create(obj, new Rect(0f, 0f, 16f, 16f), new Vector2(0.5f, 0.5f)); } private static void DrawWaterDrop(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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) int num = 8; int num2 = 10; int num3 = 4; int num4 = 5; for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { float num5 = (float)(j - num) / (float)num3; float num6 = (float)(i - num2) / (float)num4; float num7 = num5 * num5 + num6 * num6; if (i < num2 && Mathf.Abs(j - num) <= Mathf.RoundToInt((float)(i - 1) * 0.55f)) { Plot(px, s, j, i, fg, outline, Mathf.Abs(j - num) == Mathf.RoundToInt((float)(i - 1) * 0.55f)); } else if (num7 <= 1f) { Plot(px, s, j, i, fg, outline, num7 > 0.75f); } } } } private static void DrawLeaf(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { float num = (float)j / (float)(s - 1); float num2 = (float)i / (float)(s - 1); float num3 = Mathf.Abs(num + num2 - 1f); float num4 = Mathf.Abs(num - num2); if (num3 < 0.55f && num4 < 0.55f) { bool edge = num3 > 0.48f || num4 > 0.48f; Plot(px, s, j, i, fg, outline, edge); if (Mathf.Abs(num + num2 - 1f) < 0.05f && Mathf.Abs(num - num2) < 0.45f) { px[i * s + j] = outline; } } } } } private static void DrawStar(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_0062: 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) int num = s / 2; int num2 = s / 2; for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { int num3 = Mathf.Abs(j - num); int num4 = Mathf.Abs(i - num2); int num5 = num3 + num4; if (num5 <= 6 && (num3 <= 1 || num4 <= 1)) { bool edge = num5 == 6 || (num3 == 1 && num4 >= 1) || (num4 == 1 && num3 >= 1); Plot(px, s, j, i, fg, outline, edge); } } } } private static void DrawCoin(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) int num = s / 2; int num2 = s / 2; int num3 = s / 2 - 1; for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { int num4 = j - num; int num5 = i - num2; int num6 = num4 * num4 + num5 * num5; if (num6 <= num3 * num3) { bool edge = num6 >= (num3 - 1) * (num3 - 1); Plot(px, s, j, i, fg, outline, edge); } } } int num7 = num; for (int k = num2 - 3; k <= num2 + 3; k++) { if (k >= 0 && k < s) { px[k * s + num7] = outline; } } px[(num2 - 2) * s + (num7 - 1)] = outline; px[(num2 - 2) * s + (num7 - 2)] = outline; px[num2 * s + (num7 + 1)] = outline; px[num2 * s + (num7 + 2)] = outline; px[(num2 + 2) * s + (num7 - 1)] = outline; px[(num2 + 2) * s + (num7 - 2)] = outline; } private static void DrawClock(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) int num = s / 2; int num2 = s / 2; int num3 = s / 2 - 1; for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { int num4 = j - num; int num5 = i - num2; int num6 = num4 * num4 + num5 * num5; if (num6 <= num3 * num3) { bool edge = num6 >= (num3 - 1) * (num3 - 1); Plot(px, s, j, i, fg, outline, edge); } } } for (int k = 0; k < 3; k++) { px[(num2 + k) * s + num] = outline; } for (int l = 0; l < 4; l++) { px[num2 * s + (num + l)] = outline; } } private static void DrawTile(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) int num = s / 2; int num2 = s / 2; for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { int num3 = Mathf.Abs(j - num); int num4 = Mathf.Abs(i - num2); if (num3 + num4 * 2 <= s - 2) { bool edge = num3 + num4 * 2 >= s - 3; Plot(px, s, j, i, fg, outline, edge); } } } } private static void DrawDiamond(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) int num = s / 2; int num2 = s / 2; for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { int num3 = Mathf.Abs(j - num) + Mathf.Abs(i - num2); if (num3 <= 6) { Plot(px, s, j, i, fg, outline, num3 == 6); } } } } private static void DrawCheck(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_0030: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) int[,] array = new int[10, 2] { { 3, 8 }, { 4, 9 }, { 5, 10 }, { 6, 11 }, { 7, 10 }, { 8, 9 }, { 9, 8 }, { 10, 7 }, { 11, 6 }, { 12, 5 } }; for (int i = 0; i < array.GetLength(0); i++) { int num = array[i, 0]; int num2 = array[i, 1]; px[num2 * s + num] = fg; px[num2 * s + num - 1] = outline; if (num2 + 1 < s) { px[(num2 + 1) * s + num] = outline; } } } private static void DrawTooltip(Color32[] px, int s, Color32 fg, Color32 outline, bool strike) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_008d: 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) //IL_0099: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) for (int i = 3; i <= 10; i++) { for (int j = 2; j <= 13; j++) { if ((j > 2 || i > 3) && (j < 13 || i > 3) && (j > 2 || i < 10) && (j < 13 || i < 10)) { bool edge = j == 2 || j == 13 || i == 3 || i == 10; Plot(px, s, j, i, fg, outline, edge); } } } px[11 * s + 5] = outline; px[11 * s + 6] = fg; px[12 * s + 5] = outline; px[7 * s + 5] = outline; px[7 * s + 8] = outline; px[7 * s + 11] = outline; if (!strike) { return; } for (int k = 0; k < s; k++) { int num = k; int num2 = s - 1 - k; if (num >= 1 && num < s - 1 && num2 >= 1 && num2 < s - 1) { px[num2 * s + num] = outline; } } } private static void DrawSprout(Color32[] px, int s, Color32 fg, Color32 outline) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) int num = s / 2; for (int i = 8; i < s - 1; i++) { px[i * s + num] = fg; px[i * s + num - 1] = outline; px[i * s + num + 1] = outline; } int[,] array = new int[6, 2] { { 4, 7 }, { 3, 8 }, { 4, 8 }, { 5, 8 }, { 5, 9 }, { 6, 9 } }; for (int j = 0; j < array.GetLength(0); j++) { int num2 = array[j, 0]; int num3 = array[j, 1]; px[num3 * s + num2] = fg; } int[,] array2 = new int[6, 2] { { 11, 7 }, { 12, 8 }, { 11, 8 }, { 10, 8 }, { 10, 9 }, { 9, 9 } }; for (int k = 0; k < array2.GetLength(0); k++) { int num4 = array2[k, 0]; int num5 = array2[k, 1]; px[num5 * s + num4] = fg; } } private static void Plot(Color32[] px, int s, int x, int y, Color32 fg, Color32 outline, bool edge) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (x >= 0 && x < s && y >= 0 && y < s) { px[y * s + x] = (edge ? outline : fg); } } private static Texture2D NewTexture(int w, int h) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown return new Texture2D(w, h, (TextureFormat)4, false) { filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)52 }; } } } namespace CropOptimizer.Patches { internal static class CharacterLoadPatch { private static CropForecast _forecast; public static void Apply(Harmony harmony, CropForecast forecast) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown if (harmony == null) { return; } _forecast = forecast; try { List list = (from m in AccessTools.GetDeclaredMethods(typeof(GameSave)) where string.Equals(m.Name, "LoadCharacter", StringComparison.Ordinal) select m).ToList(); if (list.Count == 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[CharacterLoadPatch] No GameSave.LoadCharacter overloads found."); } return; } MethodInfo methodInfo = AccessTools.Method(typeof(CharacterLoadPatch), "OnAfterLoadCharacter", (Type[])null, (Type[])null); if (methodInfo == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[CharacterLoadPatch] Postfix method not found."); } return; } foreach (MethodInfo item in list) { harmony.Patch((MethodBase)item, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[CharacterLoadPatch] Patched {list.Count} GameSave.LoadCharacter overload(s)."); } } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("[CharacterLoadPatch] Failed to patch GameSave.LoadCharacter: " + ex.Message)); } } } private static void OnAfterLoadCharacter() { try { if (_forecast != null) { _forecast.Clear(); } else { Plugin.Instance?._forecast?.Clear(); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[CharacterLoadPatch] Forecast clear failed: " + ex.Message)); } } } } internal static class CropGrowthPatch { private static CropForecast _forecast; private static bool _growthMembersResolved; private static MemberInfo _daysLeftMember; private static MemberInfo _percentGrownMember; private static MemberInfo _fullyGrownMember; private static MemberInfo _currentStageMember; private static MemberInfo _totalGrowthMember; private static MemberInfo _growthDaysMember; private static MemberInfo _fertilizedMember; private static MemberInfo _manaInfusedMember; private static MemberInfo _cropPositionMember; private static bool _itemMembersResolved; private static Type _itemDatabaseType; private static MethodInfo _itemDatabaseGetItemMethod; private static object _itemDatabaseItemsContainer; private static MethodInfo _dictTryGetValueMethod; private static PropertyInfo _dictIndexerProperty; private static PropertyInfo _itemInfoDatabaseInstanceProperty; private static FieldInfo _allItemSellInfosField; private static bool _qualityMembersResolved; private static MemberInfo _cropDataMember; private static MemberInfo _qualityMember; private static bool _loggedEtaFallback; private static bool _loggedSellFallback; private static bool _loggedQualityFallback; private static float _resolvedEtaHoursCache; private static float _resolvedQualityMultiplierCache; private static int _resolvedProjectedSellGoldCache; private const BindingFlags AllMemberFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; public static void Apply(Harmony harmony, CropForecast forecast) { //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Expected O, but got Unknown _forecast = forecast; Type type = AccessTools.TypeByName("Wish.Crop"); if (type == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[CropGrowthPatch] Wish.Crop not found"); } return; } List methodsToPatch = new List(); TryAdd(AccessTools.Method(type, "SetCropSprite", Type.EmptyTypes, (Type[])null)); Type type2 = AccessTools.TypeByName("Wish.DecorationPositionData"); if (type2 != null) { TryAdd(AccessTools.Method(type, "SetMeta", new Type[1] { type2 }, (Type[])null)); } TryAdd(AccessTools.Method(type, "Water", Type.EmptyTypes, (Type[])null)); TryAdd(AccessTools.Method(type, "Grow", new Type[1] { typeof(float) }, (Type[])null)); TryAdd(AccessTools.Method(type, "UpdateGrowth", Type.EmptyTypes, (Type[])null)); TryAdd(AccessTools.Method(type, "GrowCrop", Type.EmptyTypes, (Type[])null)); methodsToPatch = (from m in methodsToPatch group m by m.MetadataToken into g select g.First()).ToList(); if (methodsToPatch.Count == 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)"[CropGrowthPatch] No Crop methods found to patch — forecast will stay empty."); } return; } MethodInfo methodInfo = AccessTools.Method(typeof(CropGrowthPatch), "OnAfterCropGrowth", (Type[])null, (Type[])null); if (methodInfo == null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[CropGrowthPatch] Postfix method not found"); } return; } int num = 0; foreach (MethodInfo item in methodsToPatch) { try { harmony.Patch((MethodBase)item, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("[CropGrowthPatch] Patched " + type.Name + "." + item.Name + "(" + string.Join(", ", from p in item.GetParameters() select p.ParameterType.Name) + ")")); } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)("[CropGrowthPatch] Skipped " + item.Name + ": " + ex.Message)); } } } if (num == 0) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogError((object)"[CropGrowthPatch] No Harmony patches applied — check game version compatibility."); } } PatchLifecycleMethods(harmony, type); void TryAdd(MethodInfo m) { if (m != null) { methodsToPatch.Add(m); } } } private static void OnAfterCropGrowth(object __instance) { if (__instance == null || _forecast == null) { return; } try { Object val = (Object)((__instance is Object) ? __instance : null); if (val == (Object)null) { return; } int instanceID = val.GetInstanceID(); CropInstanceRegistry.Register(val); bool resolved; float nextHarvestEtaHours = (TryResolveEtaHours(__instance, out resolved) ? Mathf.Max(0f, _resolvedEtaHoursCache) : 24f); bool resolved2; float qualityMultiplier = (TryResolveQualityMultiplier(__instance, out resolved2) ? _resolvedQualityMultiplierCache : 1f); bool resolved3; int projectedSellGold = (TryResolveProjectedSellGold(__instance, qualityMultiplier, out resolved3) ? _resolvedProjectedSellGoldCache : 0); TryGetHarvestItemId(__instance, out var itemId); if (!resolved && !_loggedEtaFallback) { _loggedEtaFallback = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)"[CropGrowthPatch] ETA reflection fallback active; using default 24h for unresolved crops."); } } if (!resolved2 && !_loggedQualityFallback) { _loggedQualityFallback = true; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)"[CropGrowthPatch] Quality reflection fallback active; using default multiplier 1.0."); } } if (!resolved3 && !_loggedSellFallback) { _loggedSellFallback = true; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogDebug((object)"[CropGrowthPatch] Sell value reflection fallback active; using default 0g for unresolved crops."); } } _forecast.UpdateCropState(instanceID, nextHarvestEtaHours, qualityMultiplier, projectedSellGold, itemId); } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogDebug((object)("[CropGrowthPatch] Update failed: " + ex.Message)); } } } private static void PatchLifecycleMethods(Harmony harmony, Type cropType) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown string[] array = new string[5] { "Harvest", "DestroyCrop", "RemoveCrop", "Die", "OnDestroy" }; MethodInfo methodInfo = AccessTools.Method(typeof(CropGrowthPatch), "OnAfterCropLifecycleEnd", (Type[])null, (Type[])null); if (methodInfo == null) { return; } string[] array2 = array; foreach (string text in array2) { try { MethodInfo methodInfo2 = AccessTools.Method(cropType, text, Type.EmptyTypes, (Type[])null); if (!(methodInfo2 == null)) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[CropGrowthPatch] Lifecycle patch skipped for " + text + ": " + ex.Message)); } } } } private static void OnAfterCropLifecycleEnd(object __instance) { Object val = (Object)((__instance is Object) ? __instance : null); if (val != null) { int instanceID = val.GetInstanceID(); CropInstanceRegistry.UnregisterById(instanceID); _forecast?.RemoveCropState(instanceID); } } internal static bool TryGetTooltipEtaHours(object cropInstance, out float etaHours, out bool resolvedFromReflection) { return TryComputeEtaHours(cropInstance, out etaHours, out resolvedFromReflection); } internal static bool TryGetTooltipHarvestItemId(object cropInstance, out int itemId) { return TryGetHarvestItemId(cropInstance, out itemId); } internal static bool TryGetItemDisplayName(int itemId, out string displayName) { displayName = null; if (itemId <= 0) { return false; } try { if (!TryGetItemDataObject(itemId, out object itemData) || itemData == null) { return false; } MemberInfo memberInfo = FindMember(itemData.GetType(), "displayName", "name", "itemName", "Name", "localizedName", "LocalizedName"); if (memberInfo == null) { return false; } object memberValue = GetMemberValue(itemData, memberInfo); if (memberValue == null) { return false; } displayName = memberValue.ToString(); return !string.IsNullOrWhiteSpace(displayName); } catch { return false; } } internal static bool TryGetTooltipQualityInfo(object cropInstance, out string label, out float multiplier) { label = "Normal"; multiplier = 1f; if (cropInstance == null) { return false; } try { EnsureQualityMembers(cropInstance.GetType()); object value = null; if (!TryReadMemberValue(cropInstance, _qualityMember, out value) || value == null) { if (!TryReadMemberValue(cropInstance, _cropDataMember, out object value2) || value2 == null) { return false; } if (!TryReadMemberValue(value2, _qualityMember, out value) || value == null) { return false; } } multiplier = MapQualityMultiplier(value); label = FormatQualityLabel(value, multiplier); return true; } catch { return false; } } private static string FormatQualityLabel(object qualityValue, float multiplier) { if (qualityValue != null) { string text = qualityValue.ToString(); if (!string.IsNullOrWhiteSpace(text)) { if (text.IndexOf("gold", StringComparison.OrdinalIgnoreCase) >= 0) { return "Gold"; } if (text.IndexOf("silver", StringComparison.OrdinalIgnoreCase) >= 0) { return "Silver"; } if (TryConvertToInt(qualityValue, out var value)) { switch (value) { case 0: return "Normal"; case 1: return "Silver"; case 2: return "Gold"; default: if (value <= 0) { return "Normal"; } return $"Tier {value}"; } } return text; } } if (multiplier >= 1.99f) { return "Gold"; } if (multiplier >= 1.49f) { return "Silver"; } return "Normal"; } internal static bool TryGetTooltipGrowthStageInfo(object cropInstance, out string stageText, out float grownRatio) { stageText = null; grownRatio = -1f; if (cropInstance == null) { return false; } try { EnsureGrowthMembers(cropInstance.GetType()); if (TryReadNumericMember(cropInstance, _percentGrownMember, out var value)) { grownRatio = Mathf.Clamp01(value); float value2; bool flag = TryReadNumericMember(cropInstance, _currentStageMember, out value2); stageText = (flag ? $"{grownRatio * 100f:0}% (stage {Mathf.Max(0f, value2):0.#})" : $"{grownRatio * 100f:0}%"); return true; } float value3; bool flag2 = TryReadNumericMember(cropInstance, _currentStageMember, out value3); float value4; bool flag3 = TryReadNumericMember(cropInstance, _totalGrowthMember, out value4); if (flag2 && flag3 && value4 > 0f) { float num = Mathf.Max(0f, value3); float num2 = Mathf.Max(num, value4); grownRatio = Mathf.Clamp01(num / num2); stageText = $"stage {num:0.#} / {num2:0.#} ({grownRatio * 100f:0}%)"; return true; } if (flag2) { stageText = $"stage {Mathf.Max(0f, value3):0.#}"; return true; } } catch { } return false; } internal static bool TryGetTooltipProjectedGold(object cropInstance, out int projectedGold, out float qualityMultiplier) { projectedGold = 0; qualityMultiplier = 1f; if (cropInstance == null) { return false; } try { if (TryGetTooltipQualityInfo(cropInstance, out string _, out float multiplier)) { qualityMultiplier = multiplier; } if (!TryGetHarvestItemId(cropInstance, out var itemId) || itemId <= 0) { return false; } if (!TryGetBaseSellPrice(itemId, out var sellPrice)) { return false; } projectedGold = Mathf.Max(0, Mathf.RoundToInt((float)sellPrice * Mathf.Max(0f, qualityMultiplier))); return true; } catch { return false; } } internal static void AppendItemExtraLines(int itemId, List lines) { if (itemId <= 0 || lines == null) { return; } try { if (TryGetItemDataObject(itemId, out object itemData) && itemData != null) { Type type = itemData.GetType(); string text = ReadStringLike(itemData, type, "seasons", "Seasons", "season", "Season", "plantableSeasons"); if (!string.IsNullOrWhiteSpace(text)) { lines.Add("Season: " + text); } bool? flag = ReadBool(itemData, type, "regrowable", "Regrowable", "regrows", "Regrows", "isRegrowable"); if (flag.HasValue) { lines.Add("Regrows: " + (flag.Value ? "yes" : "no")); } if (ReadBool(itemData, type, "isSeed", "IsSeed", "seedItem", "SeedItem").GetValueOrDefault()) { lines.Add("Type: Seed"); } if (ReadBool(itemData, type, "isMuseum", "IsMuseum", "museumItem", "museumDonation").GetValueOrDefault()) { lines.Add("Museum: yes"); } if (ReadBool(itemData, type, "isBundleItem", "bundleItem", "inBundle", "IsBundleItem").GetValueOrDefault()) { lines.Add("Bundle: yes"); } } } catch { } } private static string ReadStringLike(object instance, Type t, params string[] names) { foreach (string text in names) { MemberInfo memberInfo = FindMember(t, text); if (memberInfo == null) { continue; } object memberValue = GetMemberValue(instance, memberInfo); if (memberValue == null) { continue; } if (memberValue is IEnumerable enumerable && !(memberValue is string)) { List list = new List(); foreach (object item in enumerable) { if (item != null) { list.Add(item.ToString()); } } if (list.Count > 0) { return string.Join(", ", list); } } else { string text2 = memberValue.ToString(); if (!string.IsNullOrWhiteSpace(text2)) { return text2; } } } return null; } private static bool? ReadBool(object instance, Type t, params string[] names) { foreach (string text in names) { MemberInfo memberInfo = FindMember(t, text); if (memberInfo == null) { continue; } object memberValue = GetMemberValue(instance, memberInfo); if (memberValue != null) { if (memberValue is bool) { return (bool)memberValue; } if (memberValue is int num) { return num != 0; } if (memberValue is float value) { return Math.Abs(value) > 0.0001f; } } } return null; } private static bool TryComputeEtaHours(object cropInstance, out float etaHours, out bool resolved) { resolved = false; etaHours = 24f; if (cropInstance == null) { return false; } try { EnsureGrowthMembers(cropInstance.GetType()); if (TryReadBoolMember(cropInstance, _fullyGrownMember, out var value) && value) { etaHours = 0f; resolved = true; return true; } if (TryReadNumericMember(cropInstance, _daysLeftMember, out var value2)) { etaHours = Mathf.Max(0f, value2 * 24f); resolved = true; return true; } if (TryReadNumericMember(cropInstance, _currentStageMember, out var value3) && TryReadNumericMember(cropInstance, _totalGrowthMember, out var value4)) { etaHours = Mathf.Max(0f, (value4 - value3) * 24f); resolved = true; return true; } if (TryReadNumericMember(cropInstance, _growthDaysMember, out var value5)) { etaHours = Mathf.Max(0f, value5 * 24f); resolved = true; return true; } } catch { } return false; } internal static bool TryReadBoolMember(object instance, MemberInfo member, out bool value) { value = false; if (!TryReadMemberValue(instance, member, out object value2) || value2 == null) { return false; } if (value2 is bool flag) { value = flag; return true; } if (value2 is int num) { value = num != 0; return true; } if (value2 is float value3) { value = Math.Abs(value3) > 0.0001f; return true; } return false; } internal static bool TryGetTooltipFertilized(object cropInstance, out bool fertilized) { fertilized = false; if (cropInstance == null) { return false; } EnsureGrowthMembers(cropInstance.GetType()); return TryReadBoolMember(cropInstance, _fertilizedMember, out fertilized); } internal static bool TryGetTooltipManaInfused(object cropInstance, out bool manaInfused) { manaInfused = false; if (cropInstance == null) { return false; } EnsureGrowthMembers(cropInstance.GetType()); return TryReadBoolMember(cropInstance, _manaInfusedMember, out manaInfused); } internal static bool TryGetTooltipFullyGrown(object cropInstance, out bool fullyGrown) { fullyGrown = false; if (cropInstance == null) { return false; } EnsureGrowthMembers(cropInstance.GetType()); return TryReadBoolMember(cropInstance, _fullyGrownMember, out fullyGrown); } internal static bool TryGetCropGridPosition(object cropInstance, out Vector2Int tile) { //IL_0001: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) tile = default(Vector2Int); if (cropInstance == null) { return false; } EnsureGrowthMembers(cropInstance.GetType()); if (!TryReadMemberValue(cropInstance, _cropPositionMember, out object value) || value == null) { return false; } if (value is Vector3Int val) { tile = new Vector2Int(((Vector3Int)(ref val)).x, ((Vector3Int)(ref val)).y); return true; } if (value is Vector2Int val2) { tile = val2; return true; } return false; } private static bool TryResolveEtaHours(object cropInstance, out bool resolved) { resolved = false; _resolvedEtaHoursCache = 24f; if (cropInstance == null) { return false; } if (!TryComputeEtaHours(cropInstance, out var etaHours, out resolved) || !resolved) { return false; } _resolvedEtaHoursCache = etaHours; return true; } private static bool TryResolveQualityMultiplier(object cropInstance, out bool resolved) { resolved = false; _resolvedQualityMultiplierCache = 1f; if (cropInstance == null) { return false; } try { EnsureQualityMembers(cropInstance.GetType()); object value = null; if (!TryReadMemberValue(cropInstance, _qualityMember, out value) || value == null) { if (!TryReadMemberValue(cropInstance, _cropDataMember, out object value2) || value2 == null) { return false; } if (!TryReadMemberValue(value2, _qualityMember, out value) || value == null) { return false; } } _resolvedQualityMultiplierCache = MapQualityMultiplier(value); resolved = true; return true; } catch { return false; } } private static bool TryResolveProjectedSellGold(object cropInstance, float qualityMultiplier, out bool resolved) { resolved = false; _resolvedProjectedSellGoldCache = 0; if (cropInstance == null) { return false; } try { EnsureItemPriceCaches(); if (!TryGetHarvestItemId(cropInstance, out var itemId) || itemId <= 0) { return false; } if (!TryGetBaseSellPrice(itemId, out var sellPrice)) { return false; } _resolvedProjectedSellGoldCache = Mathf.Max(0, Mathf.RoundToInt((float)sellPrice * Mathf.Max(0f, qualityMultiplier))); resolved = true; return true; } catch { return false; } } private static void EnsureGrowthMembers(Type cropType) { if (!_growthMembersResolved && !(cropType == null)) { _growthMembersResolved = true; _daysLeftMember = FindMember(cropType, "DaysLeft", "daysLeft", "remainingDays", "daysRemaining"); _percentGrownMember = FindMember(cropType, "PercentGrown", "percentGrown"); _fullyGrownMember = FindMember(cropType, "FullyGrown", "fullyGrown"); _currentStageMember = FindMember(cropType, "_stage", "stage", "currentGrowthStage", "growthStage"); _totalGrowthMember = FindMember(cropType, "totalGrowthTime", "daysToGrow"); _growthDaysMember = FindMember(cropType, "growthDays"); _fertilizedMember = FindMember(cropType, "Fertilized", "fertilized"); _manaInfusedMember = FindMember(cropType, "ManaInfused", "manaInfused"); _cropPositionMember = FindMember(cropType, "Position", "position"); } } private static void EnsureQualityMembers(Type cropType) { if (!_qualityMembersResolved && !(cropType == null)) { _qualityMembersResolved = true; _qualityMember = FindMember(cropType, "quality", "cropQuality", "_quality"); _cropDataMember = FindMember(cropType, "data", "Data", "_data"); if (_qualityMember == null && _cropDataMember != null && TryReadMemberType(_cropDataMember, out Type type)) { _qualityMember = FindMember(type, "quality", "cropQuality", "_quality"); } } } private static void EnsureItemPriceCaches() { if (_itemMembersResolved) { return; } _itemMembersResolved = true; _itemDatabaseType = AccessTools.TypeByName("Wish.ItemDatabase"); if (_itemDatabaseType != null) { _itemDatabaseGetItemMethod = _itemDatabaseType.GetMethod("GetItem", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(int) }, null) ?? _itemDatabaseType.GetMethod("GetItemData", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(int) }, null); FieldInfo fieldInfo = _itemDatabaseType.GetField("Items", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) ?? _itemDatabaseType.GetField("items", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) ?? _itemDatabaseType.GetField("_items", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); if (fieldInfo != null) { _itemDatabaseItemsContainer = fieldInfo.GetValue(null); if (_itemDatabaseItemsContainer != null) { Type type = _itemDatabaseItemsContainer.GetType(); _dictTryGetValueMethod = type.GetMethod("TryGetValue", BindingFlags.Instance | BindingFlags.Public); _dictIndexerProperty = type.GetProperty("Item", BindingFlags.Instance | BindingFlags.Public); } } } Type type2 = AccessTools.TypeByName("Wish.ItemInfoDatabase"); if (type2 != null) { _itemInfoDatabaseInstanceProperty = type2.GetProperty("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) ?? type2.GetProperty("instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); _allItemSellInfosField = type2.GetField("allItemSellInfos", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); } } private static bool TryGetItemDataObject(int itemId, out object itemData) { itemData = null; try { EnsureItemPriceCaches(); if (_itemDatabaseGetItemMethod != null) { itemData = _itemDatabaseGetItemMethod.Invoke(null, new object[1] { itemId }); if (itemData != null) { return true; } } if (_itemDatabaseItemsContainer != null) { if (_dictTryGetValueMethod != null) { object[] array = new object[2] { itemId, null }; if ((bool)_dictTryGetValueMethod.Invoke(_itemDatabaseItemsContainer, array)) { itemData = array[1]; if (itemData != null) { return true; } } } else if (_dictIndexerProperty != null) { itemData = _dictIndexerProperty.GetValue(_itemDatabaseItemsContainer, new object[1] { itemId }); if (itemData != null) { return true; } } } if (_itemInfoDatabaseInstanceProperty != null && _allItemSellInfosField != null) { object value = _itemInfoDatabaseInstanceProperty.GetValue(null); if (((value != null) ? _allItemSellInfosField.GetValue(value) : null) is IDictionary dictionary && dictionary.Contains(itemId)) { itemData = dictionary[itemId]; if (itemData != null) { return true; } } } } catch { } return false; } private static bool TryGetHarvestItemId(object cropInstance, out int itemId) { itemId = 0; if (cropInstance == null) { return false; } try { object obj = ReflectionHelper.GetInstanceValue(cropInstance, "_cropItem") ?? ReflectionHelper.GetInstanceValue(cropInstance, "cropItem") ?? ReflectionHelper.GetInstanceValue(cropInstance, "item"); if (obj == null) { return false; } object instanceValue = ReflectionHelper.GetInstanceValue(obj, "id"); if (instanceValue == null) { instanceValue = ReflectionHelper.GetInstanceValue(obj, "ID"); } if (instanceValue == null) { return false; } return TryConvertToInt(instanceValue, out itemId) && itemId > 0; } catch { return false; } } private static bool TryGetBaseSellPrice(int itemId, out int sellPrice) { sellPrice = 0; try { EnsureItemPriceCaches(); if (_itemInfoDatabaseInstanceProperty != null && _allItemSellInfosField != null) { object value = _itemInfoDatabaseInstanceProperty.GetValue(null); if (((value != null) ? _allItemSellInfosField.GetValue(value) : null) is IDictionary dictionary && dictionary.Contains(itemId) && TryExtractSellPrice(dictionary[itemId], out sellPrice)) { return true; } } if (_itemDatabaseGetItemMethod != null && TryExtractSellPrice(_itemDatabaseGetItemMethod.Invoke(null, new object[1] { itemId }), out sellPrice)) { return true; } if (_itemDatabaseItemsContainer != null) { object itemData = null; if (_dictTryGetValueMethod != null) { object[] array = new object[2] { itemId, null }; if ((bool)_dictTryGetValueMethod.Invoke(_itemDatabaseItemsContainer, array)) { itemData = array[1]; } } else if (_dictIndexerProperty != null) { itemData = _dictIndexerProperty.GetValue(_itemDatabaseItemsContainer, new object[1] { itemId }); } if (TryExtractSellPrice(itemData, out sellPrice)) { return true; } } } catch { return false; } return false; } private static bool TryExtractSellPrice(object itemData, out int sellPrice) { sellPrice = 0; if (itemData == null) { return false; } MemberInfo memberInfo = FindMember(itemData.GetType(), "sellPrice", "sellGold", "sell", "price", "Price"); if (memberInfo == null) { return false; } object memberValue = GetMemberValue(itemData, memberInfo); if (memberValue == null) { return false; } try { if (memberValue is float num) { sellPrice = Mathf.Max(0, Mathf.RoundToInt(num)); } else if (memberValue is double num2) { sellPrice = Mathf.Max(0, Mathf.RoundToInt((float)num2)); } else { sellPrice = Mathf.Max(0, Mathf.RoundToInt(Convert.ToSingle(memberValue))); } return true; } catch { return false; } } private static float MapQualityMultiplier(object qualityValue) { if (qualityValue == null) { return 1f; } string text = qualityValue.ToString(); if (!string.IsNullOrWhiteSpace(text)) { if (text.IndexOf("gold", StringComparison.OrdinalIgnoreCase) >= 0) { return 2f; } if (text.IndexOf("silver", StringComparison.OrdinalIgnoreCase) >= 0) { return 1.5f; } } if (TryConvertToInt(qualityValue, out var value)) { if (value >= 2) { return 2f; } if (value == 1) { return 1.5f; } } return 1f; } private static MemberInfo FindMember(Type type, params string[] names) { if (type == null || names == null) { return null; } Type type2 = type; while (type2 != null) { foreach (string name in names) { try { FieldInfo field = type2.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); if (field != null) { return field; } } catch { } try { PropertyInfo property = type2.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); if (property != null) { return property; } } catch { } } type2 = type2.BaseType; } return null; } private static bool TryReadMemberType(MemberInfo member, out Type type) { type = null; if (member is FieldInfo fieldInfo) { type = fieldInfo.FieldType; return type != null; } if (member is PropertyInfo propertyInfo) { type = propertyInfo.PropertyType; return type != null; } return false; } private static bool TryReadMemberValue(object instance, MemberInfo member, out object value) { value = null; if (instance == null || member == null) { return false; } value = GetMemberValue(instance, member); return value != null; } private static object GetMemberValue(object instance, MemberInfo member) { try { if (member is FieldInfo fieldInfo) { return fieldInfo.GetValue(instance); } if (member is PropertyInfo propertyInfo) { return propertyInfo.GetValue(instance, null); } } catch { } return null; } private static bool TryReadNumericMember(object instance, MemberInfo member, out float value) { value = 0f; if (!TryReadMemberValue(instance, member, out object value2)) { return false; } try { value = Convert.ToSingle(value2); return true; } catch { return false; } } private static bool TryReadIntMember(object instance, MemberInfo member, out int value) { value = 0; if (!TryReadMemberValue(instance, member, out object value2)) { return false; } return TryConvertToInt(value2, out value); } private static bool TryConvertToInt(object raw, out int value) { value = 0; if (raw == null) { return false; } try { value = Convert.ToInt32(raw); return true; } catch { return false; } } } } namespace CropOptimizer.Integration { internal sealed class BirthdayIntegration { private const string PluginGuid = "com.azraelgodking.squirrelsbirthdayreminder"; public bool IsAvailable { get { if (Chainloader.PluginInfos != null) { return Chainloader.PluginInfos.ContainsKey("com.azraelgodking.squirrelsbirthdayreminder"); } return false; } } public IReadOnlyList GetSuggestedReserveProduce() { if (!IsAvailable) { return Array.Empty(); } try { if (!Chainloader.PluginInfos.TryGetValue("com.azraelgodking.squirrelsbirthdayreminder", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { return Array.Empty(); } MethodInfo methodInfo = AccessTools.Method(((object)value.Instance).GetType(), "GetManager", Type.EmptyTypes, (Type[])null); if (methodInfo == null) { return Array.Empty(); } object obj = methodInfo.Invoke(null, null); if (obj == null) { return Array.Empty(); } Type type = obj.GetType(); object obj2 = AccessTools.Property(type, "HasBirthdays")?.GetValue(obj); if (obj2 is bool && !(bool)obj2) { return Array.Empty(); } if (!(AccessTools.Property(type, "TodaysBirthdays")?.GetValue(obj) is IList list) || list.Count == 0) { return Array.Empty(); } List list2 = new List(); foreach (object item in list) { if (item != null) { Type type2 = item.GetType(); string arg = (AccessTools.Property(type2, "NPCName")?.GetValue(item) as string) ?? "?"; if (AccessTools.Property(type2, "AllLovedGifts")?.GetValue(item) is IList list3 && list3.Count > 0) { list2.Add($"{arg}: {list3[0]}"); } } } return list2; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[CropOptimizer] Birthday integration: " + ex.Message)); } return Array.Empty(); } } } internal sealed class TodoIntegration { private const string PluginGuid = "com.azraelgodking.sunhaventodo"; public bool IsAvailable { get { if (Chainloader.PluginInfos != null) { return Chainloader.PluginInfos.ContainsKey("com.azraelgodking.sunhaventodo"); } return false; } } public void PublishHarvestReadyTodo(int readyTileCount) { if (!IsAvailable || readyTileCount <= 0) { return; } try { if (!Chainloader.PluginInfos.TryGetValue("com.azraelgodking.sunhaventodo", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { return; } Type type = ((object)value.Instance).GetType(); MethodInfo methodInfo = AccessTools.Method(type, "GetTodoManager", Type.EmptyTypes, (Type[])null); if (methodInfo == null) { return; } object obj = methodInfo.Invoke(null, null); if (obj == null) { return; } Assembly assembly = type.Assembly; Type type2 = assembly.GetType("SunhavenTodo.Data.TodoPriority"); Type type3 = assembly.GetType("SunhavenTodo.Data.TodoCategory"); if (!(type2 == null) && !(type3 == null)) { MethodInfo methodInfo2 = AccessTools.Method(obj.GetType(), "AddTodo", new Type[4] { typeof(string), typeof(string), type2, type3 }, (Type[])null); if (!(methodInfo2 == null)) { methodInfo2.Invoke(obj, new object[4] { $"Harvest ready: {readyTileCount} tiles", "Auto-generated by Crop Optimizer", Enum.Parse(type2, "Normal"), Enum.Parse(type3, "Farming") }); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[CropOptimizer] Todo integration: " + ex.Message)); } } } } internal sealed class VaultIntegration { public bool IsAvailable => Chainloader.PluginInfos.ContainsKey("com.azraelgodking.thevault"); public bool TryRegisterProjectedValueCurrency() { if (!IsAvailable || VaultModApiBridge.Instance == null || !VaultModApiBridge.Instance.IsVaultReady) { return false; } return VaultModApiBridge.Instance.RegisterCustomCurrency("crop_projected_value", "Projected Crop Value", -1, false); } } } namespace CropOptimizer.Data { internal sealed class CropForecast { internal readonly struct CropState { public float NextHarvestEtaHours { get; } public float QualityMultiplier { get; } public int ProjectedSellGold { get; } public int ItemId { get; } public CropState(float nextHarvestEtaHours, float qualityMultiplier, int projectedSellGold, int itemId) { NextHarvestEtaHours = nextHarvestEtaHours; QualityMultiplier = qualityMultiplier; ProjectedSellGold = projectedSellGold; ItemId = itemId; } } public readonly struct CropTypeSummary { public int ItemId { get; } public int TotalGold { get; } public int CropCount { get; } public CropTypeSummary(int itemId, int totalGold, int cropCount) { ItemId = itemId; TotalGold = totalGold; CropCount = cropCount; } } private readonly Dictionary _cropStateByInstanceId = new Dictionary(); private int _runningProjectedSellTotal; public void UpdateCropState(int cropInstanceId, float nextHarvestEtaHours, float qualityMultiplier, int projectedSellGold, int itemId = 0) { int num = Math.Max(0, projectedSellGold); if (_cropStateByInstanceId.TryGetValue(cropInstanceId, out var value)) { _runningProjectedSellTotal -= Math.Max(0, value.ProjectedSellGold); } _cropStateByInstanceId[cropInstanceId] = new CropState(nextHarvestEtaHours, qualityMultiplier, projectedSellGold, itemId); _runningProjectedSellTotal += num; } public IReadOnlyDictionary Snapshot() { return _cropStateByInstanceId; } public int GetProjectedSellTotal() { return _runningProjectedSellTotal; } public bool TryGetState(int cropInstanceId, out CropState state) { return _cropStateByInstanceId.TryGetValue(cropInstanceId, out state); } public bool RemoveCropState(int cropInstanceId) { if (!_cropStateByInstanceId.TryGetValue(cropInstanceId, out var value)) { return false; } _runningProjectedSellTotal -= Math.Max(0, value.ProjectedSellGold); return _cropStateByInstanceId.Remove(cropInstanceId); } public List GetTopCropsByValue(int count = 5) { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair item in _cropStateByInstanceId) { CropState value = item.Value; if (value.ItemId > 0) { if (!dictionary.TryGetValue(value.ItemId, out var value2)) { value2 = (0, 0); } dictionary[value.ItemId] = (value2.Item1 + Math.Max(0, value.ProjectedSellGold), value2.Item2 + 1); } } return (from kvp in dictionary select new CropTypeSummary(kvp.Key, kvp.Value.totalGold, kvp.Value.cropCount) into s orderby s.TotalGold descending select s).Take(count).ToList(); } public void Clear() { _cropStateByInstanceId.Clear(); _runningProjectedSellTotal = 0; } } internal static class CropInstanceRegistry { private static readonly HashSet ActiveCropIds = new HashSet(); public static void Register(Object cropObject) { if (!(cropObject == (Object)null)) { ActiveCropIds.Add(cropObject.GetInstanceID()); } } public static void Unregister(Object cropObject) { if (!(cropObject == (Object)null)) { ActiveCropIds.Remove(cropObject.GetInstanceID()); } } public static void UnregisterById(int instanceId) { if (instanceId != 0) { ActiveCropIds.Remove(instanceId); } } public static bool IsKnownActive(int instanceId) { return ActiveCropIds.Contains(instanceId); } } public static class CropOptimizerDataProvider { public readonly struct CropTypeSummary { public int ItemId { get; } public int TotalGold { get; } public int CropCount { get; } public CropTypeSummary(int itemId, int totalGold, int cropCount) { ItemId = itemId; TotalGold = totalGold; CropCount = cropCount; } } public static string GetSummary() { return Plugin.GetHudSummary(); } public static List GetTopCrops(int count = 5) { List topCrops = Plugin.GetTopCrops(count); List list = new List(topCrops.Count); foreach (CropForecast.CropTypeSummary item in topCrops) { list.Add(new CropTypeSummary(item.ItemId, item.TotalGold, item.CropCount)); } return list; } public static bool TryGetCropDisplayName(int itemId, out string name) { return CropGrowthPatch.TryGetItemDisplayName(itemId, out name); } } } namespace CropOptimizer.Config { internal sealed class CropOptimizerConfig { public ConfigEntry Enabled { get; } public ConfigEntry HudEnabled { get; } public ConfigEntry HudScale { get; } public ConfigEntry HudPositionX { get; } public ConfigEntry HudPositionY { get; } public ConfigEntry ToggleHudKey { get; } public ConfigEntry HoverTooltipEnabled { get; } public ConfigEntry HoverTooltipMaxWorldDistance { get; } public ConfigEntry DebugLogging { get; } public ConfigEntry CheckForUpdates { get; } public CropOptimizerConfig(ConfigFile config) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Expected O, but got Unknown Enabled = config.Bind("General", "Enabled", true, "Enable Crop Optimizer"); CheckForUpdates = config.Bind("General", "CheckForUpdates", true, "Check for Crop Optimizer updates on startup via GitHub Pages."); HudEnabled = config.Bind("HUD", "Enabled", true, "Show Crop Optimizer HUD"); HudScale = config.Bind("HUD", "Scale", 1f, new ConfigDescription("HUD scale", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 2.5f), Array.Empty())); HudPositionX = config.Bind("HUD", "PositionX", 20f, "HUD window X (pixels); updated when you drag the panel)"); HudPositionY = config.Bind("HUD", "PositionY", 80f, "HUD window Y (pixels); updated when you drag the panel)"); ToggleHudKey = config.Bind("HUD", "ToggleKey", (KeyCode)284, "Toggle crop HUD"); HoverTooltipEnabled = config.Bind("HUD", "HoverTooltip", true, "Experimental: show crop info (crop name, water/fertil guess, ETA) when the mouse is near a crop in-world"); HoverTooltipMaxWorldDistance = config.Bind("HUD", "HoverTooltipMaxWorldDistance", 5f, new ConfigDescription("Max distance from mouse (world units) to treat a crop as hovered", (AcceptableValueBase)(object)new AcceptableValueRange(0.25f, 16f), Array.Empty())); DebugLogging = config.Bind("Debug", "DebugLogging", false, "Enable debug logging"); } } }