Finished all of the updates for the SDV 1.4 update!

This commit is contained in:
JoshuaNavarro 2019-12-13 16:00:06 -08:00
parent b4b1f79816
commit 1453812b50
14 changed files with 548 additions and 433 deletions

View File

@ -2260,5 +2260,48 @@ namespace Omegasis.HappyBirthday
}
return b.ToString();
}
public string getBirthdayMessage(string NPC)
{
if (Game1.player.friendshipData.ContainsKey(NPC))
{
if (Game1.player.getSpouse() != null) {
if (Game1.player.getSpouse().Name.Equals(NPC))
{
if (string.IsNullOrEmpty(this.spouseBirthdayWishes[NPC]))
{
return this.generateSpouseBirthdayDialogue(NPC);
}
else
{
return this.spouseBirthdayWishes[NPC];
}
}
else
{
if (this.birthdayWishes.ContainsKey(NPC))
{
return this.birthdayWishes[NPC];
}
}
}
}
else
{
if (this.birthdayWishes.ContainsKey(NPC))
{
return this.birthdayWishes[NPC];
}
else
{
return "Happy birthday @!";
}
}
return "Happy birthday @!";
}
}
}

View File

@ -636,7 +636,7 @@ namespace Omegasis.HappyBirthday.Framework
conditions.Add(new StardustCore.Events.Preconditions.NPCSpecific.DatingNPC(shane));
EventHelper e = new EventHelper("BirthdayDating:Shane", 19960, conditions, new EventStartData("playful", 26, 15, new EventStartData.FarmerData(19, 18, EventHelper.FacingDirection.Left), new List<EventStartData.NPCData>() {
new EventStartData.NPCData(shane,3,19, EventHelper.FacingDirection.Down),
new EventStartData.NPCData(shane,25,16, EventHelper.FacingDirection.Down),
}));
e.globalFadeIn();

View File

@ -27,6 +27,7 @@ namespace Omegasis.HappyBirthday.Framework
/// <summary>The player's current birthday season.</summary>
private string BirthdaySeason;
private string seasonName;
/// <summary>The player's current birthday day.</summary>
private int BirthdayDay;
@ -46,6 +47,7 @@ namespace Omegasis.HappyBirthday.Framework
: base(Game1.viewport.Width / 2 - (632 + IClickableMenu.borderWidth * 2) / 2, Game1.viewport.Height / 2 - (600 + IClickableMenu.borderWidth * 2) / 2 - Game1.tileSize, 632 + IClickableMenu.borderWidth * 2, 600 + IClickableMenu.borderWidth * 2 + Game1.tileSize)
{
this.BirthdaySeason = HappyBirthday.Config.translationInfo.getTranslatedString(season);
this.seasonName = season;
this.BirthdayDay = day;
this.OnChanged = onChanged;
this.SetUpPositions();
@ -147,8 +149,9 @@ namespace Omegasis.HappyBirthday.Framework
case "Fall":
case "Winter":
this.BirthdaySeason = HappyBirthday.Config.translationInfo.getTranslatedString(name);
this.OnChanged(this.BirthdaySeason, this.BirthdayDay);
Game1.activeClickableMenu = new BirthdayMenu(this.BirthdaySeason, this.BirthdayDay, this.OnChanged);
this.seasonName = name;
this.OnChanged(this.seasonName, this.BirthdayDay);
Game1.activeClickableMenu = new BirthdayMenu(this.seasonName, this.BirthdayDay, this.OnChanged);
break;
// OK button
@ -160,8 +163,8 @@ namespace Omegasis.HappyBirthday.Framework
default:
this.BirthdayDay = Convert.ToInt32(name);
this.OnChanged(this.BirthdaySeason, this.BirthdayDay);
Game1.activeClickableMenu = new BirthdayMenu(this.BirthdaySeason, this.BirthdayDay, this.OnChanged);
this.OnChanged(this.seasonName, this.BirthdayDay);
Game1.activeClickableMenu = new BirthdayMenu(this.seasonName, this.BirthdayDay, this.OnChanged);
break;
}
Game1.playSound("coin");
@ -174,7 +177,7 @@ namespace Omegasis.HappyBirthday.Framework
public override void receiveLeftClick(int x, int y, bool playSound = true)
{
//If the season is not selected then the day buttons can't be clicked. Thanks to @Potato#5266 on the SDV discord for this tip.
if (string.IsNullOrEmpty(this.BirthdaySeason)==false)
if (string.IsNullOrEmpty(this.seasonName)==false)
{
foreach (ClickableTextureComponent button in this.DayButtons)
{
@ -199,7 +202,7 @@ namespace Omegasis.HappyBirthday.Framework
if (this.OkButton.containsPoint(x, y))
{
if (this.BirthdaySeason == "" || this.BirthdayDay == 0) return;
if (this.seasonName == "" || this.BirthdayDay == 0) return;
this.HandleButtonClick(this.OkButton.name);
this.OkButton.scale -= 0.25f;
this.OkButton.scale = Math.Max(0.75f, this.OkButton.scale);
@ -247,7 +250,7 @@ namespace Omegasis.HappyBirthday.Framework
//Game1.player.FarmerSprite.draw(b, new Vector2((this.xPositionOnScreen + Game1.tileSize + Game1.tileSize * 2 / 3 - 2), (this.yPositionOnScreen + IClickableMenu.borderWidth + IClickableMenu.spaceToClearTopBorder - Game1.tileSize / 4)),1f);
// draw day buttons
if (string.IsNullOrEmpty(this.BirthdaySeason)==false)
if (string.IsNullOrEmpty(this.seasonName)==false)
{
foreach (ClickableTextureComponent button in this.DayButtons)
button.draw(b);
@ -273,7 +276,7 @@ namespace Omegasis.HappyBirthday.Framework
}
// draw OK button
if (this.BirthdayDay != 0 && this.BirthdaySeason != "")
if (this.BirthdayDay != 0 && this.seasonName != "")
this.OkButton.draw(b);
else
{

View File

@ -220,11 +220,10 @@ namespace Omegasis.HappyBirthday.Framework
public string getTranslatedString(string Key)
{
if (string.IsNullOrEmpty(Key)) return "";
if (Key.Equals("Birthday"))
{
string s = Game1.content.LoadString("Strings\\UI:Billboard_Birthday");
s = s.Split(' ')[0];
string s = Game1.content.LoadString("Strings\\UI:Profile_Birthday");
return s;
}
if (Key.Equals("Spring") || Key.Equals("spring"))

View File

@ -96,6 +96,7 @@ namespace Omegasis.HappyBirthday
helper.Events.Multiplayer.PeerDisconnected += this.Multiplayer_PeerDisconnected;
helper.Events.GameLoop.GameLaunched += this.GameLoop_GameLaunched;
helper.Events.Player.Warped += this.Player_Warped;
helper.Events.GameLoop.ReturnedToTitle += this.GameLoop_ReturnedToTitle;
ModHelper = this.Helper;
ModMonitor = this.Monitor;
@ -105,6 +106,11 @@ namespace Omegasis.HappyBirthday
}
private void GameLoop_ReturnedToTitle(object sender, ReturnedToTitleEventArgs e)
{
this.eventManager = new EventManager();
}
private void Player_Warped(object sender, WarpedEventArgs e)
{
if (e.NewLocation == Game1.getLocationFromName("CommunityCenter"))
@ -416,132 +422,8 @@ namespace Omegasis.HappyBirthday
if ((Game1.player.getFriendshipHeartLevelForNPC(Game1.currentSpeaker.Name) < Config.minimumFriendshipLevelForBirthdayWish)) return;
if (Game1.activeClickableMenu is StardewValley.Menus.DialogueBox && this.VillagerQueue[Game1.currentSpeaker.Name].hasGivenBirthdayWish==false && (Game1.player.getFriendshipHeartLevelForNPC(Game1.currentSpeaker.Name) >= Config.minimumFriendshipLevelForBirthdayWish))
{
IReflectedField < Dialogue > cDialogue= this.Helper.Reflection.GetField<Dialogue>((Game1.activeClickableMenu as DialogueBox), "characterDialogue", true);
IReflectedField<List<string>> dialogues = this.Helper.Reflection.GetField<List<string>>((Game1.activeClickableMenu as DialogueBox), "dialogues", true);
string dialogueMessage = "";
if (Game1.player.getSpouse() != null)
{
if (Game1.player.getSpouse().Name.Equals(Game1.currentSpeaker.Name))
{
if (this.messages.spouseBirthdayWishes.ContainsKey(Game1.currentSpeaker.Name))
{
dialogueMessage = this.messages.spouseBirthdayWishes[Game1.currentSpeaker.Name];
if (string.IsNullOrEmpty(dialogueMessage))
{
dialogueMessage = this.messages.generateSpouseBirthdayDialogue(Game1.currentSpeaker.Name);
}
}
else
{
dialogueMessage = this.messages.generateSpouseBirthdayDialogue(Game1.currentSpeaker.Name);
if (string.IsNullOrEmpty(dialogueMessage))
{
dialogueMessage = "Happy Birthday @!";
}
}
}
else
{
if (this.messages.birthdayWishes.ContainsKey(Game1.currentSpeaker.Name))
{
dialogueMessage = this.messages.birthdayWishes[Game1.currentSpeaker.Name];
}
else
{
dialogueMessage = "Happy Birthday @!";
}
}
}
else
{
if (this.messages.birthdayWishes.ContainsKey(Game1.currentSpeaker.Name))
{
dialogueMessage = this.messages.birthdayWishes[Game1.currentSpeaker.Name];
}
else
{
dialogueMessage = "Happy Birthday @!";
}
}
dialogueMessage = dialogueMessage.Replace("@", Game1.player.Name);
if (dialogues.GetValue().Contains(dialogueMessage))
{
string name = Game1.currentSpeaker.Name;
this.VillagerQueue[Game1.currentSpeaker.Name].hasGivenBirthdayWish = true;
/*
if (this.IsBirthday() && this.VillagerQueue[Game1.currentSpeaker.Name].hasGivenBirthdayGift==false && Game1.player.getFriendshipHeartLevelForNPC(name) >= Config.minNeutralFriendshipGiftLevel)
{
try
{
this.giftManager.SetNextBirthdayGift(Game1.currentSpeaker.Name);
this.Monitor.Log("Setting next birthday gift.");
}
catch (Exception ex)
{
this.Monitor.Log(ex.ToString(), LogLevel.Error);
}
}
*/
return;
}
if (cDialogue.GetValue() != null)
{
if (cDialogue.GetValue().getCurrentDialogue() == dialogueMessage)
{
string name = Game1.currentSpeaker.Name;
this.VillagerQueue[Game1.currentSpeaker.Name].hasGivenBirthdayWish = true;
if (this.IsBirthday() && this.VillagerQueue[Game1.currentSpeaker.Name].hasGivenBirthdayGift == false && Game1.player.getFriendshipHeartLevelForNPC(name) >= Config.minNeutralFriendshipGiftLevel)
{
try
{
this.giftManager.SetNextBirthdayGift(Game1.currentSpeaker.Name);
this.Monitor.Log("Setting next birthday gift. 3");
}
catch (Exception ex)
{
this.Monitor.Log(ex.ToString(), LogLevel.Error);
}
}
return;
}
}
Dialogue d;
if (Game1.player.getSpouse() != null)
{
if (this.messages.spouseBirthdayWishes.ContainsKey(Game1.currentSpeaker.Name))
{
if (string.IsNullOrEmpty(this.messages.spouseBirthdayWishes[Game1.currentSpeaker.Name])== false){
d = new Dialogue(this.messages.spouseBirthdayWishes[Game1.currentSpeaker.Name], Game1.currentSpeaker);
}
else
{
d =new Dialogue(this.messages.generateSpouseBirthdayDialogue(Game1.currentSpeaker.Name),Game1.currentSpeaker);
}
}
else
{
d = new Dialogue("Happy Birthday @!", Game1.currentSpeaker);
}
}
else
{
if (this.messages.birthdayWishes.ContainsKey(Game1.currentSpeaker.Name))
{
d = new Dialogue(this.messages.birthdayWishes[Game1.currentSpeaker.Name], Game1.currentSpeaker);
}
else
{
d = new Dialogue("Happy Birthday @!", Game1.currentSpeaker);
}
}
//IReflectedField < Dialogue > cDialogue= this.Helper.Reflection.GetField<Dialogue>((Game1.activeClickableMenu as DialogueBox), "characterDialogue", true);
//IReflectedField<List<string>> dialogues = this.Helper.Reflection.GetField<List<string>>((Game1.activeClickableMenu as DialogueBox), "dialogues", true);
Game1.currentSpeaker.resetCurrentDialogue();
Game1.currentSpeaker.resetSeasonalDialogue();
this.Helper.Reflection.GetMethod(Game1.currentSpeaker, "loadCurrentDialogue", true).Invoke();
@ -558,7 +440,8 @@ namespace Omegasis.HappyBirthday
this.Monitor.Log(ex.ToString(), LogLevel.Error);
}
}
Game1.activeClickableMenu = new DialogueBox(d);
Game1.activeClickableMenu = new DialogueBox(new Dialogue(this.messages.getBirthdayMessage(Game1.currentSpeaker.Name),Game1.currentSpeaker));
this.VillagerQueue[Game1.currentSpeaker.Name].hasGivenBirthdayWish = true;
// Set birthday gift for the player to recieve from the npc they are currently talking with.
@ -612,7 +495,8 @@ namespace Omegasis.HappyBirthday
this.CheckedForBirthday = false;
// load settings
this.MigrateLegacyData();
//
//this.MigrateLegacyData();
this.PlayerData = this.Helper.Data.ReadJsonFile<PlayerData>(this.DataFilePath) ?? new PlayerData();
if (HappyBirthday.Config.autoSetTranslation)
@ -622,7 +506,7 @@ namespace Omegasis.HappyBirthday
if (PlayerBirthdayData != null)
{
ModMonitor.Log("Send all birthday information from " + Game1.player.Name);
//ModMonitor.Log("Send all birthday information from " + Game1.player.Name);
MultiplayerSupport.SendBirthdayInfoToOtherPlayers();
}
@ -715,6 +599,7 @@ namespace Omegasis.HappyBirthday
this.eventManager.addEvent(birthdayDating_Harvey);
this.eventManager.addEvent(birthdayDating_Elliott);
this.eventManager.addEvent(birthdayDating_Sam);
this.eventManager.addEvent(birthdayDating_Alex);
this.eventManager.addEvent(birthdayDating_Shane);
if (Game1.player.eventsSeen.Contains(communityCenterJunimoBirthday.getEventID()))
{
@ -815,7 +700,7 @@ namespace Omegasis.HappyBirthday
string starMessage = BirthdayMessages.GetTranslatedString("Happy Birthday: Star Message");
ModMonitor.Log(starMessage);
//ModMonitor.Log(starMessage);
Messages.ShowStarMessage(starMessage);
MultiplayerSupport.SendBirthdayMessageToOtherPlayers();
@ -912,7 +797,6 @@ namespace Omegasis.HappyBirthday
}
}
if (Game1.player.CanReadJunimo())
{
Game1.player.mailbox.Add("birthdayJunimos");
@ -925,64 +809,10 @@ namespace Omegasis.HappyBirthday
{
if (npc is Child || npc is Horse || npc is Junimo || npc is Monster || npc is Pet)
continue;
//Add in birthday dialogues for npc.
try
{
if (Game1.player.getFriendshipHeartLevelForNPC(npc.Name) >= Config.minimumFriendshipLevelForBirthdayWish)
{
bool spouseMessage = false; //Used to determine if there is a valid spouse message for the player. If false load in the generic birthday wish.
//Check if npc name is spouse's name. If no spouse then add in generic dialogue.
if (this.messages.spouseBirthdayWishes.ContainsKey(npc.Name) && Game1.player.isMarried())
{
if (Game1.player.getSpouse().Name.Equals(npc.Name))
{
//this.Monitor.Log("Spouse Checks out");
//Check to see if spouse message exists.
if (!string.IsNullOrEmpty(this.messages.spouseBirthdayWishes[npc.Name]))
{
spouseMessage = true;
string message = this.messages.spouseBirthdayWishes[npc.Name];
if (string.IsNullOrEmpty(message))
{
message = this.messages.generateSpouseBirthdayDialogue(npc.Name);
}
Dialogue d = new Dialogue(message, npc);
npc.CurrentDialogue.Push(d);
if (npc.CurrentDialogue.ElementAt(0) != d) npc.setNewDialogue(message);
}
else
{
string message = this.messages.generateSpouseBirthdayDialogue(npc.Name);
Dialogue d = new Dialogue(message, npc);
npc.CurrentDialogue.Push(d);
if (npc.CurrentDialogue.ElementAt(0) != d) npc.setNewDialogue(message);
spouseMessage = true;
this.Monitor.Log("No spouse message???", LogLevel.Warn);
}
}
}
if (!spouseMessage)
{
//Load in
Dialogue d = new Dialogue(this.messages.birthdayWishes[npc.Name], npc);
npc.CurrentDialogue.Push(d);
if (npc.CurrentDialogue.Peek() != d) npc.setNewDialogue(this.messages.birthdayWishes[npc.Name]);
}
}
}
catch
{
if (Game1.player.getFriendshipHeartLevelForNPC(npc.Name) >= Config.minimumFriendshipLevelForBirthdayWish)
{
Dialogue d = new Dialogue("Happy Birthday @!", npc);
npc.CurrentDialogue.Push(d);
if (npc.CurrentDialogue.Peek() != d)
npc.setNewDialogue("Happy Birthday @!");
}
}
string message = this.messages.getBirthdayMessage(npc.Name);
Dialogue d = new Dialogue(message, npc);
npc.CurrentDialogue.Push(d);
if (npc.CurrentDialogue.ElementAt(0) != d) npc.setNewDialogue(message);
}
}
}
@ -1034,9 +864,10 @@ namespace Omegasis.HappyBirthday
{
return
this.PlayerData.BirthdayDay == Game1.dayOfMonth
&& this.PlayerData.BirthdaySeason.Equals(Game1.currentSeason);
&& this.PlayerData.BirthdaySeason.ToLower().Equals(Game1.currentSeason.ToLower());
}
/*
/// <summary>Migrate the legacy settings for the current player.</summary>
private void MigrateLegacyData()
{
@ -1072,5 +903,6 @@ namespace Omegasis.HappyBirthday
}
}
}
*/
}
}

View File

@ -1,7 +1,7 @@
{
"Name": "Happy Birthday",
"Author": "Alpha_Omegasis",
"Version": "1.10.0",
"Version": "1.10.4",
"Description": "Adds the farmer's birthday to the game.",
"UniqueID": "Omegasis.HappyBirthday",
"EntryDll": "HappyBirthday.dll",

View File

@ -0,0 +1,239 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using StardewValley;
namespace Omegasis.SaveAnywhere.Framework
{
public static class NPCExtensions
{
/// <summary>
/// Fill in the npcs schedule with a billion end points and hope they find their way again?
/// </summary>
/// <param name="npc"></param>
public static void fillInSchedule(this NPC npc)
{
if (npc.Schedule == null) return;
IDictionary<string, string> rawSchedule = GetRawSchedule(npc.Name);
if (rawSchedule == null)
return;
string schedulekey = GetScheduleKey(npc);
rawSchedule.TryGetValue(schedulekey, out string scheduleForTheDay);
if (string.IsNullOrEmpty(scheduleForTheDay))
{
return;
}
// parse entries
string[] entries = scheduleForTheDay.Split('/');
//make a class to get all of the schedule info.....
int scheduleIndex = 0;
SortedDictionary<int, SchedulePathInfo> actualScheduleData = new SortedDictionary<int, SchedulePathInfo>(); //time of day and the the path info.
for (int i = 0; i < entries.Length; i++)
{
string[] fields = entries[i].Split(' ');
// handle GOTO command
if (fields[0].Equals("GOTO"))
{
rawSchedule.TryGetValue(fields[1], out scheduleForTheDay);
entries = scheduleForTheDay.Split('/');
i = -1;
}
SchedulePathInfo info2 = new SchedulePathInfo(entries[i]);
if (info2.timeToGoTo == 0) continue;
else
{
actualScheduleData.Add(info2.timeToGoTo, info2);
}
}
int index = 0;
List<KeyValuePair<int, SchedulePathInfo>> scheduleData = actualScheduleData.ToList();
scheduleData.OrderBy(i => i.Key);
for (int time = 600; time <= 2600; time += 10)
{
if (index >= scheduleData.Count)
{
if (actualScheduleData.ContainsKey(time) == false)
{
actualScheduleData.Add(time, scheduleData[scheduleData.Count-1].Value);
continue;
}
}
if (index == scheduleData.Count - 1)
{
if (actualScheduleData.ContainsKey(time)==false)
{
actualScheduleData.Add(time, scheduleData[index].Value);
}
}
else
{
if (time == scheduleData[index + 1].Key)
{
index++;
continue;
}
else
{
if (actualScheduleData.ContainsKey(time) == false)
{
actualScheduleData.Add(time, scheduleData[index].Value);
}
}
}
}
//SaveAnywhere.ModMonitor.Log("Count of schedule size is: " + npc.Name + " " + actualScheduleData.Count, StardewModdingAPI.LogLevel.Info);
//npc.checkSchedule(Game1.timeOfDay);
SchedulePathInfo info = actualScheduleData[Game1.timeOfDay];
SchedulePathDescription schedulePathDescription;
schedulePathDescription = SaveAnywhere.ModHelper.Reflection
.GetMethod(npc, "pathfindToNextScheduleLocation")
.Invoke<SchedulePathDescription>(npc.currentLocation.Name, npc.getTileX(), npc.getTileY(), info.endMap, info.endX, info.endY, info.endDirection, info.endBehavior, info.endMessage);
npc.DirectionsToNewLocation = schedulePathDescription;
npc.controller = new PathFindController(npc.DirectionsToNewLocation.route, npc, Utility.getGameLocationOfCharacter(npc))
{
finalFacingDirection = npc.DirectionsToNewLocation.facingDirection,
endBehaviorFunction = null
};
}
/// <summary>Get an NPC's raw schedule data from the XNB files.</summary>
/// <param name="npcName">The NPC name whose schedules to read.</param>
/// <returns>Returns the NPC schedule if found, else <c>null</c>.</returns>
private static IDictionary<string, string> GetRawSchedule(string npcName)
{
try
{
return Game1.content.Load<Dictionary<string, string>>($"Characters\\schedules\\{npcName}");
}
catch
{
return null;
}
}
private static string GetScheduleKey(NPC npc)
{
string str = Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth);
string scheduleKey = "";
if (npc.Name.Equals("Penny") && (str.Equals("Tue") || str.Equals("Wed") || str.Equals("Fri")) || (npc.Name.Equals("Maru") && (str.Equals("Tue") || str.Equals("Thu")) || npc.Name.Equals("Harvey") && (str.Equals("Tue") || str.Equals("Thu"))))
{
scheduleKey = "marriageJob";
}
if (!Game1.isRaining && npc.hasMasterScheduleEntry("marriage_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth)))
{
scheduleKey = "marriage_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth);
}
if (npc.hasMasterScheduleEntry(Game1.currentSeason + "_" + Game1.dayOfMonth.ToString()))
{
scheduleKey = Game1.currentSeason + "_" + Game1.dayOfMonth.ToString();
}
int playerFriendshipLevel1 = Utility.GetAllPlayerFriendshipLevel(npc);
if (playerFriendshipLevel1 >= 0)
playerFriendshipLevel1 /= 250;
for (; playerFriendshipLevel1 > 0; --playerFriendshipLevel1)
{
if (npc.hasMasterScheduleEntry(Game1.dayOfMonth.ToString() + "_" + (object)playerFriendshipLevel1))
scheduleKey = Game1.dayOfMonth.ToString() + "_" + (object)playerFriendshipLevel1;
}
if (npc.hasMasterScheduleEntry(Game1.dayOfMonth.ToString()))
scheduleKey = Game1.dayOfMonth.ToString();
if (npc.Name.Equals("Pam") && Game1.player.mailReceived.Contains("ccVault"))
scheduleKey = "bus";
if (Game1.isRaining)
{
if (Game1.random.NextDouble() < 0.5 && npc.hasMasterScheduleEntry("rain2"))
scheduleKey = "rain2";
if (npc.hasMasterScheduleEntry("rain"))
scheduleKey = "rain";
}
List<string> stringList = new List<string>()
{
Game1.currentSeason,
Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth)
};
int playerFriendshipLevel2 = Utility.GetAllPlayerFriendshipLevel(npc);
if (playerFriendshipLevel2 >= 0)
playerFriendshipLevel2 /= 250;
while (playerFriendshipLevel2 > 0)
{
stringList.Add(string.Empty + (object)playerFriendshipLevel2);
if (npc.hasMasterScheduleEntry(string.Join("_", (IEnumerable<string>)stringList)))
{
scheduleKey = string.Join("_", (IEnumerable<string>)stringList);
break;
}
--playerFriendshipLevel2;
stringList.RemoveAt(stringList.Count - 1);
}
if (npc.hasMasterScheduleEntry(string.Join("_", (IEnumerable<string>)stringList)))
scheduleKey = string.Join("_", (IEnumerable<string>)stringList);
if (npc.hasMasterScheduleEntry(Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth)))
scheduleKey = Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth);
if (npc.hasMasterScheduleEntry(Game1.currentSeason))
scheduleKey = Game1.currentSeason;
if (npc.hasMasterScheduleEntry("spring_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth)))
scheduleKey = "spring_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth);
stringList.RemoveAt(stringList.Count - 1);
stringList.Add("spring");
int playerFriendshipLevel3 = Utility.GetAllPlayerFriendshipLevel(npc);
if (playerFriendshipLevel3 >= 0)
playerFriendshipLevel3 /= 250;
while (playerFriendshipLevel3 > 0)
{
stringList.Add(string.Empty + (object)playerFriendshipLevel3);
if (npc.hasMasterScheduleEntry(string.Join("_", (IEnumerable<string>)stringList)))
scheduleKey = string.Join("_", (IEnumerable<string>)stringList);
--playerFriendshipLevel3;
stringList.RemoveAt(stringList.Count - 1);
}
if (npc.hasMasterScheduleEntry("spring"))
scheduleKey = "spring";
else
{
scheduleKey = "";
}
return scheduleKey;
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Omegasis.SaveAnywhere.Framework
{
public class SaveAnywhereAPI
{
public SaveAnywhereAPI()
{
}
public void addBeforeSaveEvent(string ID, Action BeforeSave)
{
SaveAnywhere.Instance.SaveManager.beforeCustomSavingBegins.Add(ID, BeforeSave);
}
public void addAfterSaveEvent(string ID, Action BeforeSave)
{
SaveAnywhere.Instance.SaveManager.afterCustomSavingCompleted.Add(ID, BeforeSave);
}
public void addAfterLoadEvent(string ID, Action BeforeSave)
{
SaveAnywhere.Instance.SaveManager.afterSaveLoaded.Add(ID, BeforeSave);
}
}
}

View File

@ -36,6 +36,9 @@ namespace Omegasis.SaveAnywhere.Framework
private NewSaveGameMenu currentSaveMenu;
public Dictionary<string, Action> beforeCustomSavingBegins;
public Dictionary<string, Action> afterCustomSavingCompleted;
public Dictionary<string, Action> afterSaveLoaded;
/*********
** Public methods
@ -50,6 +53,10 @@ namespace Omegasis.SaveAnywhere.Framework
this.Reflection = reflection;
this.OnLoaded = onLoaded;
this.beforeCustomSavingBegins = new Dictionary<string, Action>();
this.afterCustomSavingCompleted = new Dictionary<string, Action>();
this.afterSaveLoaded = new Dictionary<string, Action>();
}
private void empty(object o, EventArgs args) { }
@ -74,7 +81,12 @@ namespace Omegasis.SaveAnywhere.Framework
{
this.currentSaveMenu.SaveComplete -= this.CurrentSaveMenu_SaveComplete;
this.currentSaveMenu = null;
SaveAnywhere.RestoreMonsters();
//AfterSave.Invoke(this, EventArgs.Empty);
foreach (var v in this.afterCustomSavingCompleted)
{
v.Value.Invoke();
}
}
/// <summary>Clear saved data.</summary>
@ -87,16 +99,31 @@ namespace Omegasis.SaveAnywhere.Framework
this.RemoveLegacyDataForThisPlayer();
}
/// <summary>
/// Checks to see if a custom save file exists for the player.
/// </summary>
/// <returns></returns>
public bool saveDataExists()
{
return File.Exists(Path.Combine(this.Helper.DirectoryPath, this.RelativeDataPath));
}
/// <summary>Initiate a game save.</summary>
public void BeginSaveData()
{
foreach(var v in this.beforeCustomSavingBegins)
{
v.Value.Invoke();
}
// save game data
Farm farm = Game1.getFarm();
if (farm.shippingBin.Any())
if (farm.getShippingBin(Game1.player)!=null)
{
Game1.activeClickableMenu = new NewShippingMenu(farm.shippingBin, this.Reflection);
farm.shippingBin.Clear();
Game1.activeClickableMenu = new NewShippingMenu(farm.getShippingBin(Game1.player), this.Reflection);
farm.getShippingBin(Game1.player).Clear();
farm.lastItemShipped = null;
this.WaitingToSave = true;
}
@ -134,6 +161,10 @@ namespace Omegasis.SaveAnywhere.Framework
this.ResumeSwimming(data);
this.SetPositions(data.Characters);
this.OnLoaded?.Invoke();
foreach (var v in this.afterSaveLoaded)
{
v.Value.Invoke();
}
// Notify other mods that load is complete
//AfterLoad.Invoke(this, EventArgs.Empty);
@ -197,7 +228,7 @@ namespace Omegasis.SaveAnywhere.Framework
{
// player
{
CharacterData data = positions.FirstOrDefault(p => p.Type == CharacterType.Player && p.Name == Game1.player.Name);
CharacterData data = positions.FirstOrDefault(p => p.Type == CharacterType.Player && p.Name.Equals(Game1.player.Name));
if (data != null)
{
Game1.player.previousLocationName = Game1.player.currentLocation.Name;

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Omegasis.SaveAnywhere.Framework
{
public class SchedulePathInfo
{
public int timeToGoTo;
public int endX;
public int endY;
public string endMap;
public int endDirection;
public string endBehavior;
public string endMessage;
public SchedulePathInfo()
{
}
public SchedulePathInfo(string RawData)
{
string[] fields = RawData.Split(' ');
try
{
this.timeToGoTo = Convert.ToInt32(fields[0]);
}
catch(Exception err)
{
return;
}
this.endMap = fields[1];
this.endX =Convert.ToInt32(fields[2]);
this.endY =Convert.ToInt32(fields[3]);
this.endDirection =Convert.ToInt32(fields[4]);
if (fields.Length >= 6)
{
if (fields[5][0] == '"')
{
this.endMessage = fields[5].Substring(fields[5].IndexOf('"'));
}
}
if (fields.Length >= 7)
{
if (fields[5][0] == '"')
{
this.endBehavior = fields[5];
}
else
{
this.endMessage = fields[6].Substring(fields[6].IndexOf('"'));
}
}
}
}
}

View File

@ -13,6 +13,8 @@ namespace Omegasis.SaveAnywhere
/// <summary>The mod entry point.</summary>
public class SaveAnywhere : Mod
{
public static SaveAnywhere Instance;
/*********
** Fields
*********/
@ -20,7 +22,8 @@ namespace Omegasis.SaveAnywhere
private ModConfig Config;
/// <summary>Provides methods for saving and loading game data.</summary>
private SaveManager SaveManager;
public SaveManager SaveManager;
/// <summary>The parsed schedules by NPC name.</summary>
private readonly IDictionary<string, string> NpcSchedules = new Dictionary<string, string>();
@ -29,7 +32,7 @@ namespace Omegasis.SaveAnywhere
private bool ShouldResetSchedules;
/// <summary>Whether we're performing a non-vanilla save (i.e. not by sleeping in bed).</summary>
private bool IsCustomSaving;
public bool IsCustomSaving;
/// <summary>Used to access the Mod's helper from other files associated with the mod.</summary>
public static IModHelper ModHelper;
@ -37,10 +40,12 @@ namespace Omegasis.SaveAnywhere
/// <summary>Used to access the Mod's monitor to allow for debug logging in other files associated with the mod.</summary>
public static IMonitor ModMonitor;
private List<Monster> monsters;
private Dictionary<GameLocation, List<Monster>> monsters;
private bool customMenuOpen;
private bool firstLoad;
/*********
** Public methods
@ -54,14 +59,27 @@ namespace Omegasis.SaveAnywhere
this.SaveManager = new SaveManager(this.Helper, this.Helper.Reflection, onLoaded: () => this.ShouldResetSchedules = true);
helper.Events.GameLoop.SaveLoaded += this.OnSaveLoaded;
helper.Events.GameLoop.Saved += this.OnSaved;
helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked;
helper.Events.GameLoop.DayStarted += this.OnDayStarted;
helper.Events.Input.ButtonPressed += this.OnButtonPressed;
helper.Events.GameLoop.ReturnedToTitle += this.GameLoop_ReturnedToTitle;
helper.Events.GameLoop.TimeChanged += this.GameLoop_TimeChanged;
ModHelper = helper;
ModMonitor = this.Monitor;
this.customMenuOpen = false;
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;
}
@ -74,32 +92,20 @@ namespace Omegasis.SaveAnywhere
private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
{
// reset state
this.IsCustomSaving = false;
this.ShouldResetSchedules = false;
// load positions
this.SaveManager.LoadData();
//this.SaveManager.ClearData();
}
/// <summary>Raised after the game finishes writing 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 OnSaved(object sender, SavedEventArgs e)
{
// clear custom data after a normal save (to avoid restoring old state)
if (!this.IsCustomSaving)
this.SaveManager.ClearData();
else
{
this.IsCustomSaving = false;
}
}
/// <summary>Raised after the game state is updated (≈60 times per second).</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
private void OnUpdateTicked(object sender, UpdateTickedEventArgs e)
{
// let save manager run background logic
if (Context.IsWorldReady)
{
@ -107,17 +113,16 @@ namespace Omegasis.SaveAnywhere
this.SaveManager.Update();
}
// reset NPC schedules
if (Context.IsWorldReady && this.ShouldResetSchedules)
if (Game1.activeClickableMenu == null && Context.IsWorldReady)
{
this.ShouldResetSchedules = false;
this.ApplySchedules();
this.IsCustomSaving = false;
}
if (Game1.activeClickableMenu == null && !this.customMenuOpen) return;
if (Game1.activeClickableMenu == null && this.customMenuOpen)
{
this.restoreMonsters();
this.customMenuOpen = false;
return;
}
@ -133,27 +138,37 @@ namespace Omegasis.SaveAnywhere
/// <summary>Saves all monsters from the game world.</summary>
private void cleanMonsters()
{
this.monsters = new List<Monster>();
this.monsters = new Dictionary<GameLocation, List<Monster>>();
foreach (var npc in Game1.player.currentLocation.characters)
foreach (GameLocation loc in Game1.locations)
{
try
this.monsters.Add(loc, new List<Monster>());
foreach (var npc in loc.characters)
{
if (npc is Monster monster)
this.monsters.Add(monster);
{
this.Monitor.Log(npc.Name);
this.monsters[loc].Add(monster);
}
}
catch { }
foreach (var monster in this.monsters[loc])
loc.characters.Remove(monster);
}
foreach (var monster in this.monsters)
Game1.player.currentLocation.characters.Remove(monster);
}
/// <summary>Adds all saved monster back into the game world.</summary>
private void restoreMonsters()
public static void RestoreMonsters()
{
foreach (var monster in this.monsters)
Game1.player.currentLocation.characters.Add(monster);
foreach (var pair in SaveAnywhere.Instance.monsters)
{
foreach (Monster m in pair.Value)
{
pair.Key.addCharacter(m);
}
}
SaveAnywhere.Instance.monsters.Clear();
}
/// <summary>Raised after the game begins a new day (including when the player loads a save).</summary>
@ -161,15 +176,41 @@ namespace Omegasis.SaveAnywhere
/// <param name="e">The event arguments.</param>
private void OnDayStarted(object sender, DayStartedEventArgs e)
{
// reload NPC schedules
this.ShouldResetSchedules = true;
//this.Monitor.Log("On day started called.", LogLevel.Info);
// update NPC schedules
this.NpcSchedules.Clear();
foreach (NPC npc in Utility.getAllCharacters())
if (this.IsCustomSaving == false)
{
if (!this.NpcSchedules.ContainsKey(npc.Name))
this.NpcSchedules.Add(npc.Name, this.ParseSchedule(npc));
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));
}
*/
}
}
@ -184,6 +225,9 @@ namespace Omegasis.SaveAnywhere
// initiate save (if valid context)
if (e.Button == this.Config.SaveKey)
{
if (Game1.eventUp) return;
if (Game1.isFestival()) return;
if (Game1.client == null)
{
this.cleanMonsters();
@ -213,199 +257,25 @@ namespace Omegasis.SaveAnywhere
return;
// apply for each NPC
foreach (NPC npc in Utility.getAllCharacters())
foreach (GameLocation loc in Game1.locations)
{
if (npc.DirectionsToNewLocation != null || npc.isMoving() || npc.Schedule == null || npc.controller != null || npc is Horse)
continue;
// get raw schedule from XNBs
IDictionary<string, string> rawSchedule = this.GetRawSchedule(npc.Name);
if (rawSchedule == null)
continue;
// get schedule data
if (!this.NpcSchedules.TryGetValue(npc.Name, out string scheduleData) || string.IsNullOrEmpty(scheduleData))
foreach (NPC npc in loc.characters)
{
//this.Monitor.Log("THIS IS AWKWARD");
continue;
}
// get schedule script
if (!rawSchedule.TryGetValue(scheduleData, out string script))
continue;
// parse entries
string[] entries = script.Split('/');
int index = 0;
foreach (string _ in entries)
{
string[] fields = entries[index].Split(' ');
// handle GOTO command
if (fields.Contains("GOTO"))
{
for (int i = 0; i < fields.Length; i++)
{
string s = fields[i];
if (s == "GOTO")
{
rawSchedule.TryGetValue(fields[i + 1], out script);
string[] newEntries = script.Split('/');
fields = newEntries[0].Split(' ');
}
}
}
// parse schedule script
SchedulePathDescription schedulePathDescription;
try
{
if (Convert.ToInt32(fields[0]) > Game1.timeOfDay) break;
string endMap = Convert.ToString(fields[1]);
int x = Convert.ToInt32(fields[2]);
int y = Convert.ToInt32(fields[3]);
int endFacingDir = Convert.ToInt32(fields[4]);
schedulePathDescription = this.Helper.Reflection
.GetMethod(npc, "pathfindToNextScheduleLocation")
.Invoke<SchedulePathDescription>(npc.currentLocation.Name, npc.getTileX(), npc.getTileY(), endMap, x, y, endFacingDir, null, null);
index++;
}
catch
{
if (npc.isVillager() == false)
continue;
}
npc.DirectionsToNewLocation = schedulePathDescription;
npc.controller = new PathFindController(npc.DirectionsToNewLocation.route, npc, Utility.getGameLocationOfCharacter(npc))
{
finalFacingDirection = npc.DirectionsToNewLocation.facingDirection,
endBehaviorFunction = null
};
npc.fillInSchedule();
continue;
}
}
}
/// <summary>Get an NPC's raw schedule data from the XNB files.</summary>
/// <param name="npcName">The NPC name whose schedules to read.</param>
/// <returns>Returns the NPC schedule if found, else <c>null</c>.</returns>
private IDictionary<string, string> GetRawSchedule(string npcName)
public override object GetApi()
{
try
{
return Game1.content.Load<Dictionary<string, string>>($"Characters\\schedules\\{npcName}");
}
catch
{
return null;
}
}
/// <summary>Load the raw schedule data for an NPC.</summary>
/// <param name="npc">The NPC whose schedule to read.</param>
private string ParseSchedule(NPC npc)
{
// set flags
if (npc.Name.Equals("Robin") || Game1.player.currentUpgrade != null)
npc.IsInvisible = false;
if (npc.Name.Equals("Willy") && Game1.stats.DaysPlayed < 2u)
npc.IsInvisible = true;
else if (npc.Schedule != null)
npc.followSchedule = true;
// read schedule data
IDictionary<string, string> schedule = this.GetRawSchedule(npc.Name);
if (schedule == null)
return "";
// do stuff
if (npc.isMarried())
{
string dayName = Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth);
if ((npc.Name.Equals("Penny") && (dayName.Equals("Tue") || dayName.Equals("Wed") || dayName.Equals("Fri"))) || (npc.Name.Equals("Maru") && (dayName.Equals("Tue") || dayName.Equals("Thu"))) || (npc.Name.Equals("Harvey") && (dayName.Equals("Tue") || dayName.Equals("Thu"))))
{
this.Helper.Reflection
.GetField<string>(npc, "nameOfTodaysSchedule")
.SetValue("marriageJob");
return "marriageJob";
}
if (!Game1.isRaining && schedule.ContainsKey("marriage_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth)))
{
this.Helper.Reflection
.GetField<string>(npc, "nameOfTodaysSchedule")
.SetValue("marriage_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth));
return "marriage_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth);
}
npc.followSchedule = false;
return null;
}
else
{
if (schedule.ContainsKey(Game1.currentSeason + "_" + Game1.dayOfMonth))
return Game1.currentSeason + "_" + Game1.dayOfMonth;
int i;
Game1.player.friendshipData.TryGetValue(npc.Name, out Friendship f);
for (i = (Game1.player.friendshipData.ContainsKey(npc.Name) ? (f.Points / 250) : -1); i > 0; i--)
{
if (schedule.ContainsKey(Game1.dayOfMonth + "_" + i))
return Game1.dayOfMonth + "_" + i;
}
if (schedule.ContainsKey(string.Empty + Game1.dayOfMonth))
return string.Empty + Game1.dayOfMonth;
if (npc.Name.Equals("Pam") && Game1.player.mailReceived.Contains("ccVault"))
return "bus";
if (Game1.isRaining)
{
if (Game1.random.NextDouble() < 0.5 && schedule.ContainsKey("rain2"))
return "rain2";
if (schedule.ContainsKey("rain"))
return "rain";
}
List<string> list = new List<string> { Game1.currentSeason, Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth) };
Game1.player.friendshipData.TryGetValue(npc.Name, out Friendship friendship);
i = (Game1.player.friendshipData.ContainsKey(npc.Name) ? (friendship.Points / 250) : -1);
while (i > 0)
{
list.Add(string.Empty + i);
if (schedule.ContainsKey(string.Join("_", list)))
{
return string.Join("_", list);
}
i--;
list.RemoveAt(list.Count - 1);
}
if (schedule.ContainsKey(string.Join("_", list)))
{
return string.Join("_", list);
}
if (schedule.ContainsKey(Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth)))
{
return Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth);
}
if (schedule.ContainsKey(Game1.currentSeason))
{
return Game1.currentSeason;
}
if (schedule.ContainsKey("spring_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth)))
{
return "spring_" + Game1.shortDayNameFromDayOfSeason(Game1.dayOfMonth);
}
list.RemoveAt(list.Count - 1);
list.Add("spring");
Game1.player.friendshipData.TryGetValue(npc.Name, out Friendship friendship2);
i = (Game1.player.friendshipData.ContainsKey(npc.Name) ? (friendship2.Points / 250) : -1);
while (i > 0)
{
list.Add(string.Empty + i);
if (schedule.ContainsKey(string.Join("_", list)))
return string.Join("_", list);
i--;
list.RemoveAt(list.Count - 1);
}
return schedule.ContainsKey("spring")
? "spring"
: null;
}
return new SaveAnywhereAPI();
}
}
}

View File

@ -84,7 +84,10 @@
<Compile Include="Framework\Models\PositionData.cs" />
<Compile Include="Framework\NewSaveGameMenu.cs" />
<Compile Include="Framework\Models\PlayerData.cs" />
<Compile Include="Framework\NPCExtensions.cs" />
<Compile Include="Framework\SaveAnywhereAPI.cs" />
<Compile Include="Framework\SaveManager.cs" />
<Compile Include="Framework\SchedulePathInfo.cs" />
<Compile Include="SaveAnywhere.cs" />
<Compile Include="Framework\NewShippingMenu.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -1,11 +1,12 @@
{
"Name": "Save Anywhere",
"Author": "Alpha_Omegasis",
"Version": "2.11.0",
"Version": "2.12.0",
"Description": "Lets you save almost anywhere.",
"UniqueID": "Omegasis.SaveAnywhere",
"EntryDll": "SaveAnywhere.dll",
"MinimumApiVersion": "2.10.1",
"UpdateKeys": [ "Nexus:444" ],
"Dependencies": [
]
}

View File

@ -1,7 +1,7 @@
{
"Name": "StardustCore",
"Author": "Alpha_Omegasis",
"Version": "2.3.0",
"Version": "2.4.0",
"Description": "A core mod that allows for other mods of mine to be run.",
"UniqueID": "Omegasis.StardustCore",
"EntryDll": "StardustCore.dll",