2018-12-30 18:00:05 +08:00
|
|
|
using System;
|
2016-11-04 02:25:53 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
2017-07-31 11:07:07 +08:00
|
|
|
using Omegasis.SaveAnywhere.Framework;
|
2016-11-04 02:25:53 +08:00
|
|
|
using StardewModdingAPI;
|
|
|
|
using StardewModdingAPI.Events;
|
2017-07-28 08:28:39 +08:00
|
|
|
using StardewValley;
|
2016-11-04 02:25:53 +08:00
|
|
|
using StardewValley.Characters;
|
2018-06-09 02:40:23 +08:00
|
|
|
using StardewValley.Monsters;
|
2016-11-04 02:25:53 +08:00
|
|
|
|
2017-07-28 08:28:39 +08:00
|
|
|
namespace Omegasis.SaveAnywhere
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2017-07-31 10:55:58 +08:00
|
|
|
/// <summary>The mod entry point.</summary>
|
2017-07-31 05:42:15 +08:00
|
|
|
public class SaveAnywhere : Mod
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
|
|
|
|
public static SaveAnywhere Instance;
|
2017-07-31 10:55:58 +08:00
|
|
|
/*********
|
2019-01-06 15:23:07 +08:00
|
|
|
** Fields
|
2017-07-31 10:55:58 +08:00
|
|
|
*********/
|
2017-08-06 11:20:48 +08:00
|
|
|
/// <summary>The mod configuration.</summary>
|
|
|
|
private ModConfig Config;
|
|
|
|
|
2017-07-31 10:55:58 +08:00
|
|
|
/// <summary>Provides methods for saving and loading game data.</summary>
|
2019-12-14 08:00:06 +08:00
|
|
|
public SaveManager SaveManager;
|
|
|
|
|
2017-07-31 10:55:58 +08:00
|
|
|
|
2017-08-06 11:00:37 +08:00
|
|
|
/// <summary>The parsed schedules by NPC name.</summary>
|
|
|
|
private readonly IDictionary<string, string> NpcSchedules = new Dictionary<string, string>();
|
|
|
|
|
2017-07-31 10:55:58 +08:00
|
|
|
/// <summary>Whether villager schedules should be reset now.</summary>
|
|
|
|
private bool ShouldResetSchedules;
|
|
|
|
|
2017-08-06 11:17:51 +08:00
|
|
|
/// <summary>Whether we're performing a non-vanilla save (i.e. not by sleeping in bed).</summary>
|
2019-12-14 08:00:06 +08:00
|
|
|
public bool IsCustomSaving;
|
2017-05-14 06:27:24 +08:00
|
|
|
|
2018-12-30 18:00:05 +08:00
|
|
|
/// <summary>Used to access the Mod's helper from other files associated with the mod.</summary>
|
2018-01-11 05:14:19 +08:00
|
|
|
public static IModHelper ModHelper;
|
2018-12-30 18:00:05 +08:00
|
|
|
|
|
|
|
/// <summary>Used to access the Mod's monitor to allow for debug logging in other files associated with the mod.</summary>
|
2018-01-11 05:14:19 +08:00
|
|
|
public static IMonitor ModMonitor;
|
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
private Dictionary<GameLocation, List<Monster>> monsters;
|
2018-06-09 02:40:23 +08:00
|
|
|
|
|
|
|
private bool customMenuOpen;
|
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
private bool firstLoad;
|
|
|
|
|
2018-12-30 18:00:05 +08:00
|
|
|
|
2017-07-31 10:55:58 +08:00
|
|
|
/*********
|
|
|
|
** Public methods
|
|
|
|
*********/
|
|
|
|
/// <summary>The mod entry point, called after the mod is first loaded.</summary>
|
|
|
|
/// <param name="helper">Provides simplified APIs for writing mods.</param>
|
2016-12-09 08:34:28 +08:00
|
|
|
public override void Entry(IModHelper helper)
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2017-08-06 11:20:48 +08:00
|
|
|
this.Config = helper.ReadConfig<ModConfig>();
|
2017-07-31 05:57:00 +08:00
|
|
|
|
2018-06-23 15:31:40 +08:00
|
|
|
this.SaveManager = new SaveManager(this.Helper, this.Helper.Reflection, onLoaded: () => this.ShouldResetSchedules = true);
|
|
|
|
|
2019-01-06 15:21:06 +08:00
|
|
|
helper.Events.GameLoop.SaveLoaded += this.OnSaveLoaded;
|
|
|
|
helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
|
|
|
|
helper.Events.GameLoop.DayStarted += this.OnDayStarted;
|
|
|
|
helper.Events.Input.ButtonPressed += this.OnButtonPressed;
|
2019-12-14 08:00:06 +08:00
|
|
|
helper.Events.GameLoop.ReturnedToTitle += this.GameLoop_ReturnedToTitle;
|
|
|
|
helper.Events.GameLoop.TimeChanged += this.GameLoop_TimeChanged;
|
2018-06-09 02:40:23 +08:00
|
|
|
|
2018-01-11 05:14:19 +08:00
|
|
|
ModHelper = helper;
|
2018-12-30 18:00:05 +08:00
|
|
|
ModMonitor = this.Monitor;
|
|
|
|
this.customMenuOpen = false;
|
2019-12-14 08:00:06 +08:00
|
|
|
Instance = this;
|
|
|
|
this.firstLoad = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void GameLoop_TimeChanged(object sender, TimeChangedEventArgs e)
|
|
|
|
{
|
|
|
|
//throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void GameLoop_ReturnedToTitle(object sender, ReturnedToTitleEventArgs e)
|
|
|
|
{
|
|
|
|
this.firstLoad = false;
|
2016-11-04 02:25:53 +08:00
|
|
|
}
|
2017-05-14 06:27:24 +08:00
|
|
|
|
2018-12-30 18:00:05 +08:00
|
|
|
|
2017-07-31 10:55:58 +08:00
|
|
|
/*********
|
|
|
|
** Private methods
|
|
|
|
*********/
|
2019-01-06 15:21:06 +08:00
|
|
|
/// <summary>Raised after the player loads a save slot and the world is initialised.</summary>
|
2017-07-31 09:58:48 +08:00
|
|
|
/// <param name="sender">The event sender.</param>
|
2019-01-06 15:21:06 +08:00
|
|
|
/// <param name="e">The event arguments.</param>
|
|
|
|
private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
|
2017-05-14 06:27:24 +08:00
|
|
|
{
|
2017-08-06 11:00:37 +08:00
|
|
|
// reset state
|
|
|
|
this.ShouldResetSchedules = false;
|
2017-07-31 10:55:58 +08:00
|
|
|
// load positions
|
2017-08-06 11:19:38 +08:00
|
|
|
this.SaveManager.LoadData();
|
2019-12-14 08:00:06 +08:00
|
|
|
//this.SaveManager.ClearData();
|
2017-05-14 06:27:24 +08:00
|
|
|
|
2017-08-06 11:17:51 +08:00
|
|
|
}
|
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
|
2019-01-06 15:21:06 +08:00
|
|
|
/// <summary>Raised after the game state is updated (≈60 times per second).</summary>
|
2017-07-31 10:55:58 +08:00
|
|
|
/// <param name="sender">The event sender.</param>
|
2019-01-06 15:21:06 +08:00
|
|
|
/// <param name="e">The event arguments.</param>
|
|
|
|
private void OnUpdateTicked(object sender, UpdateTickedEventArgs e)
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
|
2017-07-31 10:55:58 +08:00
|
|
|
// let save manager run background logic
|
|
|
|
if (Context.IsWorldReady)
|
2018-05-19 05:24:59 +08:00
|
|
|
{
|
2018-12-30 18:00:05 +08:00
|
|
|
if (!Game1.player.IsMainPlayer) return;
|
2017-07-31 10:55:58 +08:00
|
|
|
this.SaveManager.Update();
|
2018-05-19 05:24:59 +08:00
|
|
|
}
|
2017-07-31 10:55:58 +08:00
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
if (Game1.activeClickableMenu == null && Context.IsWorldReady)
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
this.IsCustomSaving = false;
|
2016-11-04 02:25:53 +08:00
|
|
|
}
|
2018-06-09 02:40:23 +08:00
|
|
|
|
2018-12-30 18:00:05 +08:00
|
|
|
if (Game1.activeClickableMenu == null && !this.customMenuOpen) return;
|
|
|
|
if (Game1.activeClickableMenu == null && this.customMenuOpen)
|
2018-06-09 02:40:23 +08:00
|
|
|
{
|
|
|
|
this.customMenuOpen = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (Game1.activeClickableMenu != null)
|
|
|
|
{
|
2019-12-31 13:37:42 +08:00
|
|
|
if (Game1.activeClickableMenu.GetType() == typeof(NewSaveGameMenuV2))
|
2018-06-09 02:40:23 +08:00
|
|
|
{
|
|
|
|
this.customMenuOpen = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-30 18:00:05 +08:00
|
|
|
/// <summary>Saves all monsters from the game world.</summary>
|
2019-12-31 13:37:42 +08:00
|
|
|
public void cleanMonsters()
|
2018-06-09 02:40:23 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
this.monsters = new Dictionary<GameLocation, List<Monster>>();
|
2018-06-09 02:40:23 +08:00
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
foreach (GameLocation loc in Game1.locations)
|
2018-06-09 02:40:23 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
this.monsters.Add(loc, new List<Monster>());
|
|
|
|
foreach (var npc in loc.characters)
|
2018-06-09 02:40:23 +08:00
|
|
|
{
|
2018-12-30 18:00:05 +08:00
|
|
|
if (npc is Monster monster)
|
2019-12-14 08:00:06 +08:00
|
|
|
{
|
|
|
|
this.Monitor.Log(npc.Name);
|
|
|
|
this.monsters[loc].Add(monster);
|
|
|
|
}
|
2018-06-09 02:40:23 +08:00
|
|
|
}
|
2019-12-14 08:00:06 +08:00
|
|
|
foreach (var monster in this.monsters[loc])
|
|
|
|
loc.characters.Remove(monster);
|
2018-06-09 02:40:23 +08:00
|
|
|
}
|
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
|
2018-06-09 02:40:23 +08:00
|
|
|
}
|
|
|
|
|
2018-12-30 18:00:05 +08:00
|
|
|
/// <summary>Adds all saved monster back into the game world.</summary>
|
2019-12-14 08:00:06 +08:00
|
|
|
public static void RestoreMonsters()
|
2018-06-09 02:40:23 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
foreach (var pair in SaveAnywhere.Instance.monsters)
|
|
|
|
{
|
|
|
|
foreach (Monster m in pair.Value)
|
|
|
|
{
|
|
|
|
pair.Key.addCharacter(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SaveAnywhere.Instance.monsters.Clear();
|
2016-11-04 02:25:53 +08:00
|
|
|
}
|
|
|
|
|
2019-01-06 15:21:06 +08:00
|
|
|
/// <summary>Raised after the game begins a new day (including when the player loads a save).</summary>
|
2017-07-31 10:55:58 +08:00
|
|
|
/// <param name="sender">The event sender.</param>
|
2019-01-06 15:21:06 +08:00
|
|
|
/// <param name="e">The event arguments.</param>
|
|
|
|
private void OnDayStarted(object sender, DayStartedEventArgs e)
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
//this.Monitor.Log("On day started called.", LogLevel.Info);
|
2016-11-04 02:25:53 +08:00
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
if (this.IsCustomSaving == false)
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
if (this.firstLoad == false)
|
|
|
|
{
|
|
|
|
this.firstLoad = true;
|
|
|
|
if (this.SaveManager.saveDataExists())
|
|
|
|
{
|
|
|
|
this.ShouldResetSchedules = false;
|
|
|
|
this.ApplySchedules();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (this.firstLoad == true)
|
|
|
|
{
|
|
|
|
|
|
|
|
this.SaveManager.ClearData(); //Clean the save state on consecutive days to ensure save files aren't lost inbetween incase the player accidently quits.
|
|
|
|
}
|
|
|
|
|
|
|
|
//this.Monitor.Log("Cleaning old save file.", LogLevel.Info);
|
|
|
|
|
|
|
|
// reload NPC schedules
|
|
|
|
this.ShouldResetSchedules = true;
|
|
|
|
|
|
|
|
// reset NPC schedules
|
|
|
|
|
|
|
|
/*
|
|
|
|
// update NPC schedules
|
|
|
|
this.NpcSchedules.Clear();
|
|
|
|
foreach (NPC npc in Utility.getAllCharacters())
|
|
|
|
{
|
|
|
|
if (!this.NpcSchedules.ContainsKey(npc.Name))
|
|
|
|
this.NpcSchedules.Add(npc.Name, this.ParseSchedule(npc));
|
|
|
|
}
|
|
|
|
*/
|
2016-11-04 02:25:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 15:21:06 +08:00
|
|
|
/// <summary>Raised after the player presses a button on the keyboard, controller, or mouse.</summary>
|
2017-07-31 10:55:58 +08:00
|
|
|
/// <param name="sender">The event sender.</param>
|
2019-01-06 15:21:06 +08:00
|
|
|
/// <param name="e">The event arguments.</param>
|
|
|
|
private void OnButtonPressed(object sender, ButtonPressedEventArgs e)
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2017-08-06 03:23:10 +08:00
|
|
|
if (!Context.IsPlayerFree)
|
2016-12-09 08:34:28 +08:00
|
|
|
return;
|
2016-11-04 02:25:53 +08:00
|
|
|
|
2017-08-06 11:22:15 +08:00
|
|
|
// initiate save (if valid context)
|
2019-01-06 15:21:06 +08:00
|
|
|
if (e.Button == this.Config.SaveKey)
|
2017-08-06 11:22:15 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
|
|
|
|
if (Game1.eventUp) return;
|
|
|
|
if (Game1.isFestival()) return;
|
2018-12-30 18:00:05 +08:00
|
|
|
if (Game1.client == null)
|
2017-08-06 11:22:15 +08:00
|
|
|
{
|
2019-12-31 13:37:42 +08:00
|
|
|
|
2018-05-19 05:24:59 +08:00
|
|
|
// validate: community center Junimos can't be saved
|
|
|
|
|
|
|
|
if (Game1.player.currentLocation.getCharacters().OfType<Junimo>().Any())
|
|
|
|
{
|
|
|
|
Game1.addHUDMessage(new HUDMessage("The spirits don't want you to save here.", HUDMessage.error_type));
|
|
|
|
return;
|
|
|
|
}
|
2017-08-06 11:22:15 +08:00
|
|
|
|
2018-05-19 05:24:59 +08:00
|
|
|
// save
|
2018-12-10 17:33:10 +08:00
|
|
|
this.IsCustomSaving = true;
|
2018-05-19 05:24:59 +08:00
|
|
|
this.SaveManager.BeginSaveData();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-30 18:00:05 +08:00
|
|
|
Game1.addHUDMessage(new HUDMessage("Only server hosts can save anywhere.", HUDMessage.error_type));
|
2018-05-19 05:24:59 +08:00
|
|
|
}
|
2017-08-06 11:22:15 +08:00
|
|
|
}
|
2017-07-31 10:55:58 +08:00
|
|
|
}
|
2017-07-31 05:42:15 +08:00
|
|
|
|
2017-07-31 10:55:58 +08:00
|
|
|
/// <summary>Apply the NPC schedules to each NPC.</summary>
|
|
|
|
private void ApplySchedules()
|
|
|
|
{
|
|
|
|
if (Game1.weatherIcon == Game1.weather_festival || Game1.isFestival() || Game1.eventUp)
|
|
|
|
return;
|
2016-11-04 02:25:53 +08:00
|
|
|
|
2017-07-31 10:55:58 +08:00
|
|
|
// apply for each NPC
|
2019-12-14 08:00:06 +08:00
|
|
|
foreach (GameLocation loc in Game1.locations)
|
2017-07-31 10:55:58 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
foreach (NPC npc in loc.characters)
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
if (npc.isVillager() == false)
|
2016-11-04 02:25:53 +08:00
|
|
|
continue;
|
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
npc.fillInSchedule();
|
|
|
|
continue;
|
2016-11-04 02:25:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-12-14 08:00:06 +08:00
|
|
|
|
2016-11-04 02:25:53 +08:00
|
|
|
}
|
|
|
|
|
2019-12-14 08:00:06 +08:00
|
|
|
public override object GetApi()
|
2016-11-04 02:25:53 +08:00
|
|
|
{
|
2019-12-14 08:00:06 +08:00
|
|
|
|
|
|
|
return new SaveAnywhereAPI();
|
2016-11-04 02:25:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|