using System.Diagnostics.CodeAnalysis; using System.Reflection; using Harmony; using StardewModdingAPI.Framework.Patching; using StardewModdingAPI.Framework.Reflection; using StardewValley; namespace StardewModdingAPI.Patches { /// A Harmony patch for the constructor which intercepts invalid dialogue lines and logs an error instead of crashing. internal class CheckEventPreconditionErrorPatch : IHarmonyPatch { /********* ** Private methods *********/ /// Writes messages to the console and log file on behalf of the game. private static IMonitor MonitorForGame; /// Local variable to store the original method. private static MethodInfo originalMethod; /// Local variable to check if the method was already arbitrated. private static bool isArbitrated; /********* ** Accessors *********/ /// A unique name for this patch. public string Name => $"{nameof(CheckEventPreconditionErrorPatch)}"; /********* ** Public methods *********/ /// Construct an instance. /// Writes messages to the console and log file on behalf of the game. /// Simplifies access to private code. public CheckEventPreconditionErrorPatch(IMonitor monitorForGame, Reflector reflector) { CheckEventPreconditionErrorPatch.MonitorForGame = monitorForGame; } /// Apply the Harmony patch. /// The Harmony instance. public void Apply(HarmonyInstance harmony) { originalMethod = AccessTools.Method(typeof(GameLocation), "checkEventPrecondition"); harmony.Patch(originalMethod, new HarmonyMethod(AccessTools.Method(this.GetType(), "Prefix"))); } /********* ** Private methods *********/ /// The method to call instead of the GameLocation.CheckEventPrecondition. /// The instance being patched. /// The return value of the original method. /// The precondition to be parsed. /// Returns whether to execute the original method. /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")] private static bool Prefix(GameLocation __instance, ref int __result, string precondition) { if (isArbitrated) { isArbitrated = false; return true; } else { isArbitrated = true; try { object _ = originalMethod.Invoke(__instance, new object[] { precondition }); __result = _ is null ? -1 : (int)_; } catch (System.Exception ex) { __result = -1; CheckEventPreconditionErrorPatch.MonitorForGame.Log($"Failed parsing event info. Event precondition: {precondition}\n{ex}", LogLevel.Error); } return false; } } } }