/// <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); }