/// <summary>Raised after the in-game clock time changes.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void OnTimeChanged(object sender, TimeChangedEventArgs e)
        {
            if (!Game1.hasLoadedGame)
            {
                return;
            }

            if (!Context.IsMainPlayer)
            {
                return;
            }

            Conditions.TenMinuteUpdate();

            if (Game1.currentLocation.IsOutdoors && Conditions.HasWeather(CurrentWeather.Lightning) && !Conditions.HasWeather(CurrentWeather.Rain) && Game1.timeOfDay < 2400)
            {
                Utility.performLightningUpdate(Game1.timeOfDay);
            }

            //queued messages clear
            if (Game1.timeOfDay == 610 && queuedMsg != null)
            {
                Game1.hudMessages.Add(queuedMsg);
                queuedMsg = null;
            }

            WeatherProcessing.ProcessHazardousCropWeather(Conditions, Game1.timeOfDay, Dice);

            ClimatesOfFerngill.WindOverrideSpeed = 0f; //reset once time has passed.
        }
        /// <summary>Raised before the game begins writes data to the save file (except the initial save creation).</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void OnSaving(object sender, SavingEventArgs e)
        {
            if (!Context.IsMainPlayer)
            {
                return;
            }
            Conditions.OnSaving();
            if (Conditions.trackerModel is null)
            {
                Conditions.trackerModel = new ClimateTracker();
            }

            this.Helper.Data.WriteSaveData("climate-tracker", Conditions.trackerModel);
            queuedMsg = WeatherProcessing.HandleOnSaving(Conditions, Dice); //handles crop death and any other weather conditions that need to be handled on save.
        }
        /// <summary>Raised after the game begins a new day (including when the player loads a save).</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void OnDayStarted(object sender, DayStartedEventArgs e)
        {
            Conditions.OnNewDay();
            IsBloodMoon = false;

            if (!Context.IsMainPlayer)
            {
                return;
            }

            WeatherProcessing.OnNewDay();
            UpdateWeatherOnNewDay();
            SetTomorrowWeather();

            WeatherSync message = Conditions.GenerateWeatherSyncMessage();

            MPHandler.SendMessage(message, "WeatherSync", modIDs: new[] { ModManifest.UniqueID });
        }
        /// <summary>Raised after a game menu is opened, closed, or replaced.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void OnMenuChanged(object sender, MenuChangedEventArgs e)
        {
            if (!Context.IsMainPlayer)
            {
                return;
            }

            // restore previous menu on close
            if (e.OldMenu is WeatherMenu && this.PreviousMenu != null)
            {
                Game1.activeClickableMenu = this.PreviousMenu;
                this.PreviousMenu         = null;
            }

            // handle new dialogue box
            else if (e.NewMenu is DialogueBox box)
            {
                double odds = Dice.NextDouble(), stormOdds = GameClimate.GetStormOdds(SDate.Now().AddDays(1), Dice);
                WeatherProcessing.HandleStormTotemInterception(box, odds, stormOdds);
            }
        }
        private void UpdateWeatherOnNewDay()
        {
            if (!Context.IsMainPlayer)
            {
                return;
            }

            if (Game1.dayOfMonth == 0) //do not run on day 0.
            {
                return;
            }

            int loopCount = 0;

            //Set Temperature for today and tommorow. Get today's conditions.
            //   If tomorrow is set, move it to today, and autoregen tomorrow.
            //   *201711 Due to changes in the object, it auto attempts to update today from tomorrow.

            if (!Conditions.IsTodayTempSet)
            {
                if (!Conditions.IsTomorrowTempSet)
                {
                    Conditions.SetTodayTemps(GameClimate.GetTemperatures(SDate.Now(), Dice));
                    if (Game1.weatherForTomorrow == Game1.weather_snow)
                    {
                        while (!WeatherConditions.IsValidWeatherForSnow(Conditions.GetTodayTemps()) && loopCount <= 1000)
                        {
                            Conditions.SetTodayTemps(GameClimate.GetTemperatures(SDate.Now(), Dice));
                            loopCount++;
                        }
                    }
                }
                else
                {
                    Conditions.SetTodayTempsFromTomorrow();
                }

                Conditions.SetTomorrowTemps(GameClimate.GetTemperatures(SDate.Now().AddDays(1), Dice));
            }


            if (WeatherOpt.Verbose)
            {
                Monitor.Log($"Updated the temperature for tommorow and today. Setting weather for today... ", LogLevel.Trace);
            }

            //if today is a festival or wedding, do not go further.
            if (Conditions.GetCurrentConditions().HasAnyFlags(CurrentWeather.Festival | CurrentWeather.Wedding))
            {
                if (WeatherOpt.Verbose)
                {
                    Monitor.Log("It is a wedding or festival today. Not attempting to run special weather or fog.");
                }

                return;
            }

            //variable rain conditions
            WeatherProcessing.DynamicRainOnNewDay(Conditions, Dice);
            if (!Conditions.IsVariableRain)
            {
                WeatherProcessing.CheckForStaticRainChanges(Conditions, Dice, GameClimate.ChanceForNonNormalRain);
            }


            if (WeatherProcessing.TestForSpecialWeather(Conditions, GameClimate.GetClimateForDate(SDate.Now())))
            {
                if (WeatherOpt.Verbose)
                {
                    Monitor.Log("Special weather created!");
                }
                Conditions.UpdateClimateTracker();
            }
        }
        private void SetTomorrowWeather()
        {
            //if tomorrow is a festival or wedding, we need to set the weather and leave.
            if (Utility.isFestivalDay(Game1.dayOfMonth + 1, Game1.currentSeason))
            {
                Game1.netWorldState.Value.WeatherForTomorrow = Game1.weatherForTomorrow = Game1.weather_festival;
                Conditions.HaltWeatherSystem();

                if (WeatherOpt.Verbose)
                {
                    Monitor.Log($"Festival tomorrow. Aborting processing.", LogLevel.Trace);
                }

                //if (WeatherOpt.Verbose) Monitor.Log(DebugOutput.ToString());
                return;
            }

            if (Game1.player.spouse != null && Game1.player.isEngaged() && Game1.player.friendshipData[Game1.player.spouse].CountdownToWedding == 1)
            {
                Game1.netWorldState.Value.WeatherForTomorrow = Game1.weatherForTomorrow = Game1.weather_wedding;
                Conditions.HaltWeatherSystem();

                if (WeatherOpt.Verbose)
                {
                    Monitor.Log($"Wedding tomorrow. Aborting processing.", LogLevel.Trace);
                }

                return;
            }

            if (WeatherUtilities.CheckForForceDay(DescriptionEngine, SDate.Now().AddDays(1), Monitor, WeatherOpt.Verbose))
            {
                Conditions.HaltWeatherSystem();
                if (WeatherOpt.Verbose)
                {
                    Monitor.Log($"The game will force tomorrow. Aborting processing.", LogLevel.Trace);
                }
                return;
            }

            if (WeatherOpt.Verbose)
            {
                Monitor.Log("Setting weather for tomorrow");
            }

            //now set tomorrow's weather
            var OddsForTheDay = GameClimate.GetClimateForDate(SDate.Now().AddDays(1));

            //get system odds
            double rainSystem = OddsForTheDay.RetrieveSystemOdds("rain");
            double windSystem = OddsForTheDay.RetrieveSystemOdds("debris");
            double sunSystem  = OddsForTheDay.RetrieveSystemOdds("sunny");

            if (!Game1.player.mailReceived.Contains("ccDoorUnlock"))
            {
                rainSystem = Math.Max(rainSystem - .1, 0);
                windSystem = Math.Max(windSystem - .1, 0);
                sunSystem  = Math.Min(sunSystem + .2, 1);
            }


            //get loose odds
            double rainDays  = OddsForTheDay.RetrieveOdds(Dice, "rain", SDate.Now().AddDays(1).Day);
            double windyDays = OddsForTheDay.RetrieveOdds(Dice, "debris", SDate.Now().AddDays(1).Day);
            double stormDays = OddsForTheDay.RetrieveOdds(Dice, "storm", SDate.Now().AddDays(1).Day);

            if (!Game1.player.mailReceived.Contains("ccDoorUnlock"))
            {
                rainDays  = Math.Max(rainDays - .1, 0);
                windyDays = Math.Max(windyDays - .1, 0);
            }

            if (WeatherOpt.Verbose)
            {
                Monitor.Log($"Weather Odds are Rain: {rainDays:N3}, Windy: {windyDays:N3}, and Storm {stormDays:N3}");
                Monitor.Log($"Weather System Odds are Rain: {rainSystem:N3}, Windy: {windSystem:N3}, and Storm {sunSystem:N3}");
            }

            //set tomorrow's weather
            if (Conditions.trackerModel.IsWeatherSystem)
            {
                double SystemContinuesOdds = WeatherProcessing.GetWeatherSystemOddsForNewDay(Conditions);
                if (WeatherOpt.Verbose)
                {
                    Monitor.Log($"Rolling system odds against {SystemContinuesOdds:N3}");
                }

                if (Dice.NextDouble() < SystemContinuesOdds)
                {
                    if (WeatherOpt.Verbose)
                    {
                        Monitor.Log($"Continuing system odds - now for {Conditions.trackerModel.WeatherSystemDays + 1} for weather {Conditions.trackerModel.WeatherSystemType}");
                    }
                    Conditions.trackerModel.WeatherSystemDays++;
                    WeatherProcessing.SetWeatherTomorrow(Conditions.trackerModel.WeatherSystemType, Dice, GameClimate, stormDays, Conditions.GetTomorrowTemps());
                }

                else
                {
                    if (WeatherOpt.Verbose)
                    {
                        Monitor.Log($"Ending system, new weather type.");
                    }
                    Conditions.trackerModel.IsWeatherSystem = false;
                    WeatherProcessing.SetWeatherNonSystemForTomorrow(Dice, GameClimate, rainDays, stormDays, windyDays, Conditions.GetTomorrowTemps());
                }
            }
            else
            {
                if (Dice.NextDouble() < WeatherOpt.WeatherSystemChance)
                {
                    if (WeatherOpt.Verbose)
                    {
                        Monitor.Log($"Rolling system odds against {WeatherOpt.WeatherSystemChance:N3}, created system.");
                    }

                    ProbabilityDistribution <string> SystemDist = new ProbabilityDistribution <string>();

                    SystemDist.AddNewCappedEndPoint(rainSystem, "rain");
                    SystemDist.AddNewCappedEndPoint(windSystem, "debris");
                    SystemDist.AddNewCappedEndPoint(sunSystem, "sunny");

                    double distOdd = Dice.NextDouble();

                    if (!(SystemDist.GetEntryFromProb(distOdd, out string Result)))
                    {
                        Result = "sunny";
                        Monitor.Log("The weather has failed to process in some manner. Falling back to [sunny]", LogLevel.Info);
                    }

                    Conditions.SetNewWeatherSystem(Result, 1);
                    WeatherProcessing.SetWeatherTomorrow(Result, Dice, GameClimate, stormDays, Conditions.GetTomorrowTemps());
                }
                else
                {
                    WeatherProcessing.SetWeatherNonSystemForTomorrow(Dice, GameClimate, rainDays, stormDays, windyDays, Conditions.GetTomorrowTemps());
                }
            }
        }
 /// <summary>Raised after the game returns to the title screen.</summary>
 /// <param name="sender">The event sender.</param>
 /// <param name="e">The event arguments.</param>
 private void OnReturnedToTitle(object sender, ReturnedToTitleEventArgs e)
 {
     Conditions.Reset();
     IsBloodMoon = false;
     WeatherProcessing.Reset();
 }
        /// <summary>The mod entry point, called after the mod is first loaded.</summary>
        /// <param name="helper">Provides simplified APIs for writing mods.</param>
        public override void Entry(IModHelper helper)
        {
            RWeatherIcon = new Rectangle();
            WeatherOpt   = helper.ReadConfig <WeatherConfig>();
            Logger       = Monitor;
            Translator   = Helper.Translation;
            Reflection   = Helper.Reflection;
            MPHandler    = Helper.Multiplayer;
            Dice         = new Xoshiro.PRNG64.XoShiRo256starstar();
            OurIcons     = new Sprites.Icons(Helper.Content);
            WeatherProcessing.Init();
            Conditions         = new WeatherConditions();
            DescriptionEngine  = new Descriptions(WeatherOpt, Helper.Translation);
            queuedMsg          = null;
            SummitRebornLoaded = false;

            if (WeatherOpt.Verbose)
            {
                Monitor.Log($"Loading climate type: {WeatherOpt.ClimateType} from file", LogLevel.Trace);
            }

            var path = Path.Combine("assets", "climates", WeatherOpt.ClimateType + ".json");

            GameClimate = helper.Data.ReadJsonFile <FerngillClimate>(path);

            if (GameClimate is null)
            {
                this.Monitor.Log($"The required '{path}' file is missing. Try reinstalling the mod to fix that.", LogLevel.Error);
                this.Monitor.Log("This mod will now disable itself.", LogLevel.Error);
                this.Disabled = true;
            }

            if (Disabled)
            {
                return;
            }

            var harmony = new Harmony("koihimenakamura.climatesofferngill");

            harmony.Patch(
                original: AccessTools.Method(typeof(GameLocation), "drawAboveAlwaysFrontLayer"),
                transpiler: new HarmonyMethod(AccessTools.Method(typeof(GameLocationPatches), "DAAFLTranspiler")));
            Monitor.Log("Patching GameLocation::drawAboveAlwaysFrontLayer with a Transpiler.", LogLevel.Trace);

            harmony.Patch(
                original: AccessTools.Method(typeof(Game1), "drawWeather"),
                prefix: new HarmonyMethod(AccessTools.Method(typeof(Game1Patches), "DrawWeatherPrefix")));
            Monitor.Log("Patching Game1::drawWeather with a prefix method.", LogLevel.Trace);

            harmony.Patch(
                original: AccessTools.Method(typeof(Game1), "updateWeather"),
                prefix: new HarmonyMethod(AccessTools.Method(typeof(Game1Patches), "UpdateWeatherPrefix")));
            Monitor.Log("Patching Game1::updateWeather with a prefix method.", LogLevel.Trace);

            harmony.Patch(
                original: AccessTools.Method(AccessTools.TypeByName("StardewModdingAPI.Framework.SGame"), "DrawImpl"),
                transpiler: new HarmonyMethod(AccessTools.Method(typeof(SGamePatches), "DrawImplTranspiler")));
            Monitor.Log("Patching SMAPI (SGame::DrawImpl) with a transpiler.", LogLevel.Trace);

            harmony.Patch(
                original: AccessTools.Constructor(AccessTools.TypeByName("StardewValley.WeatherDebris"), new[] { typeof(Vector2), typeof(int), typeof(float), typeof(float), typeof(float) }),
                postfix: new HarmonyMethod(AccessTools.Method(typeof(WeatherDebrisPatches), "CtorPostfix")));
            Monitor.Log("Patching WeatherDebris's constructor method with a postfix method.", LogLevel.Trace);

            harmony.Patch(
                original: AccessTools.Method(typeof(WeatherDebris), "draw"),
                prefix: new HarmonyMethod(AccessTools.Method(typeof(WeatherDebrisPatches), "DrawPrefix")));
            Monitor.Log("Patching WeatherDebris::draw with a prefix method.", LogLevel.Trace);

            harmony.Patch(
                original: AccessTools.Method(typeof(WeatherDebris), "update", new[] { typeof(bool) }),
                prefix: new HarmonyMethod(AccessTools.Method(typeof(WeatherDebrisPatches), "UpdatePrefix")));
            Monitor.Log("Patching WeatherDebris::draw with a prefix method.", LogLevel.Trace);

            //INSERT DNT CHECKS HERE
            var modManifest = Helper.ModRegistry.Get("knakamura.dynamicnighttime");

            if (modManifest != null)
            {
                if (modManifest.Manifest.Version.IsOlderThan("1.2.11"))
                {
                    Monitor.Log("WARNING: Overcast weather may not work correctly unless you are running Dynamic Night Time 1.2.11 or later", LogLevel.Alert);
                }
            }
            else
            {
                harmony.Patch(
                    original: AccessTools.Method(typeof(Game1), "UpdateGameClock"),
                    postfix: new HarmonyMethod(AccessTools.Method(typeof(GameClockPatch), "Postfix")));
                Monitor.Log("Patching Game1::UpdateGameClock with a Postfix method. (Used only when Climates is installed and DNT is not.)", LogLevel.Trace);
            }

            ConsoleCommands.Init();
            SanityCheckConfigOptions();

            //subscribe to events
            var events = helper.Events;

            events.GameLoop.DayStarted            += OnDayStarted;
            events.GameLoop.Saving                += OnSaving;
            events.GameLoop.TimeChanged           += OnTimeChanged;
            events.Display.MenuChanged            += OnMenuChanged;
            events.GameLoop.UpdateTicked          += OnUpdateTicked;
            events.GameLoop.GameLaunched          += OnGameLaunched;
            events.GameLoop.OneSecondUpdateTicked += OnOneSecondUpdateTicked;
            events.GameLoop.ReturnedToTitle       += OnReturnedToTitle;
            events.GameLoop.SaveLoaded            += OnSaveLoaded;
            events.Display.RenderedWorld          += Display_RenderedWorld;
            //events.Display.RenderingHud += OnRenderingHud;
            events.Display.RenderedHud            += OnRenderedHud;
            events.Input.ButtonPressed            += OnButtonPressed;
            events.Multiplayer.ModMessageReceived += OnModMessageRecieved;

            //console commands
            helper.ConsoleCommands
            .Add("world_tmrwweather", helper.Translation.Get("console-text.desc_tmrweather"), ConsoleCommands.TomorrowWeatherChangeFromConsole)
            .Add("world_setweather", helper.Translation.Get("console-text.desc_setweather"), ConsoleCommands.WeatherChangeFromConsole)
            .Add("debug_clearspecial", "debug command to clear special weathers", ConsoleCommands.ClearSpecial)
            .Add("debug_weatherstatus", "!", ConsoleCommands.OutputWeather)
            .Add("debug_sswa", "Show Special Weather", ConsoleCommands.ShowSpecialWeather)
            .Add("debug_vrainc", "Set Rain Amt.", ConsoleCommands.SetRainAmt)
            .Add("debug_raindef", "Set Rain Deflection.", ConsoleCommands.SetRainDef)

            /*
             *  .Add("debug_setwindchance", "Set Wind Chance", ConsoleCommands.SetWindChance)
             *  .Add("debug_setwindtresh", "Set Wind Threshold", ConsoleCommands.SetWindThreshold)
             *  .Add("debug_setwindrange", "Set Wind Range", ConsoleCommands.SetWindRange)
             *  .Add("debug_raintotal", "Get Rain Total", ConsoleCommands.DisplayRainTotal)
             *  .Add("debug_getcurrentwind", "Show wind values", ConsoleCommands.ShowWind)
             *  .Add("debug_resetwind", "Reset Global Wind", ConsoleCommands.ResetGlobalWind)
             */
            .Add("debug_printClimate", "Print Climate Tracker Data", ConsoleCommands.DisplayClimateTrackerData);
        }
        /// <summary>The mod entry point, called after the mod is first loaded.</summary>
        /// <param name="helper">Provides simplified APIs for writing mods.</param>
        public override void Entry(IModHelper helper)
        {
            RWeatherIcon = new Rectangle();
            WeatherOpt   = helper.ReadConfig <WeatherConfig>();
            Logger       = Monitor;
            Translator   = Helper.Translation;
            Reflection   = Helper.Reflection;
            MPHandler    = Helper.Multiplayer;
            Dice         = new MersenneTwister();
            OurIcons     = new Sprites.Icons(Helper.Content);
            WeatherProcessing.Init();
            Conditions         = new WeatherConditions();
            DescriptionEngine  = new Descriptions(WeatherOpt, Helper.Translation);
            queuedMsg          = null;
            SummitRebornLoaded = false;

            if (WeatherOpt.Verbose)
            {
                Monitor.Log($"Loading climate type: {WeatherOpt.ClimateType} from file", LogLevel.Trace);
            }

            var path = Path.Combine("assets", "climates", WeatherOpt.ClimateType + ".json");

            GameClimate = helper.Data.ReadJsonFile <FerngillClimate>(path);

            if (GameClimate is null)
            {
                this.Monitor.Log($"The required '{path}' file is missing. Try reinstalling the mod to fix that.", LogLevel.Error);
                this.Monitor.Log("This mod will now disable itself.", LogLevel.Error);
                this.Disabled = true;
            }

            if (Disabled)
            {
                return;
            }

            var harmony = HarmonyInstance.Create("koihimenakamura.climatesofferngill");

            harmony.PatchAll(Assembly.GetExecutingAssembly());

            ConsoleCommands.Init();

            MethodInfo    GameLocationDAAFL = AccessTools.Method(typeof(GameLocation), "drawAboveAlwaysFrontLayer");
            HarmonyMethod DAAFLTranspiler   = new HarmonyMethod(AccessTools.Method(typeof(GameLocationPatches), "DAAFLTranspiler"));

            Monitor.Log($"Patching {GameLocationDAAFL} with Transpiler: {DAAFLTranspiler}", LogLevel.Trace);;
            harmony.Patch(GameLocationDAAFL, transpiler: DAAFLTranspiler);

            MethodInfo    GameDrawW         = AccessTools.Method(typeof(Game1), "drawWeather");
            HarmonyMethod DrawWeatherPrefix = new HarmonyMethod(AccessTools.Method(typeof(Game1Patches), "DrawWeatherPrefix"));

            Monitor.Log($"Patching {GameDrawW} with Prefix: {DrawWeatherPrefix}", LogLevel.Trace);;
            harmony.Patch(GameDrawW, prefix: DrawWeatherPrefix);

            Type          t             = AccessTools.TypeByName("StardewModdingAPI.Framework.SGame");
            MethodInfo    SGameDrawImpl = AccessTools.Method(t, "DrawImpl");
            HarmonyMethod DrawTrans     = new HarmonyMethod(AccessTools.Method(typeof(SGamePatches), "DrawImplTranspiler"));

            Monitor.Log($"Patching {SGameDrawImpl} with Transpiler: {DrawTrans}", LogLevel.Trace);
            harmony.Patch(SGameDrawImpl, transpiler: DrawTrans);

            t = AccessTools.TypeByName("StardewValley.WeatherDebris");
            var           DebrisConstructor = AccessTools.Constructor(t, new[] { typeof(Vector2), typeof(int), typeof(float), typeof(float), typeof(float) });
            HarmonyMethod CtorPatch         = new HarmonyMethod(AccessTools.Method(typeof(WeatherDebrisPatches), "CtorPostfix"));

            Monitor.Log($"Patching {DebrisConstructor} with Postfix: {CtorPatch}", LogLevel.Trace);
            harmony.Patch(DebrisConstructor, postfix: CtorPatch);

            MethodInfo    DebrisDraw  = AccessTools.Method(typeof(WeatherDebris), "draw");
            HarmonyMethod DebrisPatch = new HarmonyMethod(AccessTools.Method(typeof(WeatherDebrisPatches), "DrawPrefix"));

            Monitor.Log($"Patching {DebrisDraw} with Prefix: {DebrisDraw}", LogLevel.Trace);
            harmony.Patch(DebrisDraw, prefix: DebrisPatch);

            MethodInfo    DebrisUpdate      = AccessTools.Method(typeof(WeatherDebris), "update", new[] { typeof(bool) });
            HarmonyMethod DebrisUpdatePatch = new HarmonyMethod(AccessTools.Method(typeof(WeatherDebrisPatches), "UpdatePrefix"));

            Monitor.Log($"Patching {DebrisUpdate} with Prefix: {DebrisUpdatePatch}", LogLevel.Trace);
            harmony.Patch(DebrisUpdate, prefix: DebrisUpdatePatch);

            //INSERT DNT CHECKS HERE
            var modManifest = Helper.ModRegistry.Get("knakamura.dynamicnighttime");

            if (modManifest != null)
            {
                if (modManifest.Manifest.Version.IsOlderThan("1.2.11"))
                {
                    Monitor.Log("WARNING: Overcast weather may not work correctly unless you are running Dynamic Night Time 1.2.11 or later", LogLevel.Alert);
                }
            }
            else
            {
                //Normal Climates patch goes here.
                MethodInfo UpdateGameClock = helper.Reflection.GetMethod(SDVUtilities.GetSDVType("Game1"), "UpdateGameClock").MethodInfo;
                MethodInfo postfixClock    = helper.Reflection.GetMethod(typeof(Patches.GameClockPatch), "Postfix").MethodInfo;
                Monitor.Log($"Postfixing {UpdateGameClock} with {postfixClock}", LogLevel.Trace);
                harmony.Patch(UpdateGameClock, null, new HarmonyMethod(postfixClock));
            }

            SanityCheckConfigOptions();

            //subscribe to events
            var events = helper.Events;

            events.GameLoop.DayStarted            += OnDayStarted;
            events.GameLoop.Saving                += OnSaving;
            events.GameLoop.TimeChanged           += OnTimeChanged;
            events.Display.MenuChanged            += OnMenuChanged;
            events.GameLoop.UpdateTicked          += OnUpdateTicked;
            events.GameLoop.GameLaunched          += OnGameLaunched;
            events.GameLoop.OneSecondUpdateTicked += OnOneSecondUpdateTicked;
            events.GameLoop.ReturnedToTitle       += OnReturnedToTitle;
            events.GameLoop.SaveLoaded            += OnSaveLoaded;
            events.Display.RenderingHud           += OnRenderingHud;
            events.Display.RenderedHud            += OnRenderedHud;
            events.Input.ButtonPressed            += OnButtonPressed;
            events.Multiplayer.ModMessageReceived += OnModMessageRecieved;
            // events.GameLoop.UpdateTicking += GameLoop_UpdateTicking;

            //console commands
            helper.ConsoleCommands
            .Add("world_tmrwweather", helper.Translation.Get("console-text.desc_tmrweather"), ConsoleCommands.TomorrowWeatherChangeFromConsole)
            .Add("world_setweather", helper.Translation.Get("console-text.desc_setweather"), ConsoleCommands.WeatherChangeFromConsole)
            .Add("debug_clearspecial", "debug command to clear special weathers", ConsoleCommands.ClearSpecial)
            .Add("debug_weatherstatus", "!", ConsoleCommands.OutputWeather)
            .Add("debug_sswa", "Show Special Weather", ConsoleCommands.ShowSpecialWeather)
            .Add("debug_vrainc", "Set Rain Amt.", ConsoleCommands.SetRainAmt)
            .Add("debug_raintotal", "Get Rain Total", ConsoleCommands.DisplayRainTotal)
            .Add("debug_printClimate", "Print Climate Tracker Data", ConsoleCommands.DisplayClimateTrackerData);
        }