2018-12-30 18:00:05 +08:00
using System ;
2016-10-20 15:01:51 +08:00
using System.Collections.Generic ;
2017-07-28 08:28:39 +08:00
using System.IO ;
2016-10-20 15:01:51 +08:00
using System.Linq ;
2018-09-19 09:04:38 +08:00
using Microsoft.Xna.Framework ;
using Microsoft.Xna.Framework.Graphics ;
2017-07-31 11:07:07 +08:00
using Omegasis.HappyBirthday.Framework ;
2016-10-20 15:01:51 +08:00
using StardewModdingAPI ;
2017-07-30 06:02:49 +08:00
using StardewModdingAPI.Events ;
2016-10-20 15:01:51 +08:00
using StardewValley ;
2017-07-30 06:02:49 +08:00
using StardewValley.Characters ;
2018-09-19 09:04:38 +08:00
using StardewValley.Menus ;
2017-07-30 06:02:49 +08:00
using StardewValley.Monsters ;
2016-10-20 15:01:51 +08:00
2017-07-28 08:28:39 +08:00
namespace Omegasis.HappyBirthday
2016-10-20 15:01:51 +08:00
{
2017-07-30 06:02:49 +08:00
/// <summary>The mod entry point.</summary>
2018-09-19 09:04:38 +08:00
public class HappyBirthday : Mod , IAssetEditor
2016-10-20 15:01:51 +08:00
{
2017-07-30 06:02:49 +08:00
/ * * * * * * * * *
2019-01-06 15:23:07 +08:00
* * Fields
2017-07-30 06:02:49 +08:00
* * * * * * * * * /
2017-08-06 03:51:44 +08:00
/// <summary>The relative path for the current player's data file.</summary>
2018-05-10 05:23:42 +08:00
private string DataFilePath ;
2017-08-06 03:51:44 +08:00
/// <summary>The absolute path for the current player's legacy data file.</summary>
2018-05-01 09:21:31 +08:00
private string LegacyDataFilePath = > Path . Combine ( this . Helper . DirectoryPath , "Player_Birthdays" , $"HappyBirthday_{Game1.player.Name}.txt" ) ;
2017-08-06 03:51:44 +08:00
/// <summary>The mod configuration.</summary>
2018-09-19 09:04:38 +08:00
public static ModConfig Config ;
2017-08-06 03:51:44 +08:00
/// <summary>The data for the current player.</summary>
2018-12-10 11:57:12 +08:00
public static PlayerData PlayerBirthdayData ;
2018-12-30 18:00:05 +08:00
/// <summary>Wrapper for static field PlayerBirthdayData;</summary>
2018-12-10 11:57:12 +08:00
public PlayerData PlayerData
{
2018-12-30 18:00:05 +08:00
get = > PlayerBirthdayData ;
set = > PlayerBirthdayData = value ;
2018-12-10 11:57:12 +08:00
}
2016-10-20 15:01:51 +08:00
2017-07-30 06:02:49 +08:00
/// <summary>Whether the player has chosen a birthday.</summary>
2017-08-06 03:51:44 +08:00
private bool HasChosenBirthday = > ! string . IsNullOrEmpty ( this . PlayerData . BirthdaySeason ) & & this . PlayerData . BirthdayDay ! = 0 ;
2016-11-08 17:19:56 +08:00
2017-07-30 06:02:49 +08:00
/// <summary>The queue of villagers who haven't given a gift yet.</summary>
2019-07-08 08:09:28 +08:00
private Dictionary < string , VillagerInfo > VillagerQueue ;
2016-10-20 15:01:51 +08:00
2017-07-30 06:02:49 +08:00
/// <summary>Whether we've already checked for and (if applicable) set up the player's birthday today.</summary>
private bool CheckedForBirthday ;
//private Dictionary<string, Dialogue> Dialogue;
//private bool SeenEvent;
2018-03-04 11:24:36 +08:00
2018-09-19 09:04:38 +08:00
public static IModHelper ModHelper ;
2018-12-06 10:06:11 +08:00
public static IMonitor ModMonitor ;
2018-12-30 18:00:05 +08:00
/// <summary>Class to handle all birthday messages for this mod.</summary>
2018-09-19 13:35:36 +08:00
public BirthdayMessages messages ;
2018-09-19 09:04:38 +08:00
2018-12-30 18:00:05 +08:00
/// <summary>Class to handle all birthday gifts for this mod.</summary>
2018-12-06 09:16:28 +08:00
public GiftManager giftManager ;
2017-07-30 06:02:49 +08:00
2018-12-30 18:00:05 +08:00
/// <summary>Checks if the current billboard is the daily quest screen or not.</summary>
2018-12-07 07:06:26 +08:00
bool isDailyQuestBoard ;
2018-12-30 18:00:05 +08:00
Dictionary < long , PlayerData > othersBirthdays ;
2018-12-10 11:57:12 +08:00
2019-01-15 04:26:36 +08:00
public static HappyBirthday Instance ;
2018-12-10 11:57:12 +08:00
2019-07-08 08:09:28 +08:00
private NPC lastSpeaker ;
2017-07-30 06:02:49 +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-10-20 15:01:51 +08:00
{
2019-01-15 04:26:36 +08:00
Instance = this ;
2018-09-19 16:37:51 +08:00
//helper.Content.AssetLoaders.Add(new PossibleGifts());
2018-09-19 13:35:36 +08:00
Config = helper . ReadConfig < ModConfig > ( ) ;
2017-08-06 03:51:44 +08:00
2019-01-06 15:21:06 +08:00
helper . Events . GameLoop . DayStarted + = this . OnDayStarted ;
helper . Events . GameLoop . UpdateTicked + = this . OnUpdateTicked ;
helper . Events . GameLoop . SaveLoaded + = this . OnSaveLoaded ;
helper . Events . GameLoop . Saving + = this . OnSaving ;
helper . Events . Input . ButtonPressed + = this . OnButtonPressed ;
helper . Events . Display . MenuChanged + = this . OnMenuChanged ;
2019-12-03 11:10:56 +08:00
2019-01-06 15:21:06 +08:00
helper . Events . Display . RenderedActiveMenu + = this . OnRenderedActiveMenu ;
helper . Events . Display . RenderedHud + = this . OnRenderedHud ;
2018-05-19 05:24:59 +08:00
//MultiplayerSupport.initializeMultiplayerSupport();
2018-12-30 18:00:05 +08:00
ModHelper = this . Helper ;
ModMonitor = this . Monitor ;
2018-09-19 13:35:36 +08:00
2018-12-30 18:00:05 +08:00
this . messages = new BirthdayMessages ( ) ;
this . giftManager = new GiftManager ( ) ;
this . isDailyQuestBoard = false ;
2018-12-10 11:57:12 +08:00
2018-12-30 18:00:05 +08:00
ModHelper . Events . Multiplayer . ModMessageReceived + = this . Multiplayer_ModMessageReceived ;
2018-12-10 13:04:41 +08:00
2018-12-30 18:00:05 +08:00
ModHelper . Events . Multiplayer . PeerDisconnected + = this . Multiplayer_PeerDisconnected ;
2018-12-10 13:04:41 +08:00
this . othersBirthdays = new Dictionary < long , PlayerData > ( ) ;
2019-01-15 04:26:36 +08:00
2018-12-30 18:00:05 +08:00
}
2018-12-10 11:57:12 +08:00
2018-12-30 18:00:05 +08:00
/// <summary>Get whether this instance can edit the given asset.</summary>
/// <param name="asset">Basic metadata about the asset being loaded.</param>
public bool CanEdit < T > ( IAssetInfo asset )
{
return asset . AssetNameEquals ( @"Data\mail" ) ;
}
/// <summary>Edit a matched asset.</summary>
/// <param name="asset">A helper which encapsulates metadata about an asset and enables changes to it.</param>
public void Edit < T > ( IAssetData asset )
{
2019-01-06 15:21:06 +08:00
IDictionary < string , string > data = asset . AsDictionary < string , string > ( ) . Data ;
2019-01-15 04:26:36 +08:00
string momMail = BirthdayMessages . GetTranslatedString ( "Mail:birthdayMom" ) ;
string dadMail = BirthdayMessages . GetTranslatedString ( "Mail:birthdayDad" ) ;
data [ "birthdayMom" ] = momMail ;
data [ "birthdayDad" ] = dadMail ;
2018-12-10 11:57:12 +08:00
}
2018-12-30 18:00:05 +08:00
/ * * * * * * * * *
* * Private methods
* * * * * * * * * /
/// <summary>Used to check for player disconnections.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
2018-12-10 13:04:41 +08:00
private void Multiplayer_PeerDisconnected ( object sender , PeerDisconnectedEventArgs e )
{
this . othersBirthdays . Remove ( e . Peer . PlayerID ) ;
}
2018-12-10 11:57:12 +08:00
private void Multiplayer_ModMessageReceived ( object sender , ModMessageReceivedEventArgs e )
{
2018-12-30 18:00:05 +08:00
if ( e . FromModID = = ModHelper . Multiplayer . ModID & & e . Type = = MultiplayerSupport . FSTRING_SendBirthdayMessageToOthers )
2018-12-10 11:57:12 +08:00
{
string message = e . ReadAs < string > ( ) ;
2018-12-30 18:00:05 +08:00
Game1 . hudMessages . Add ( new HUDMessage ( message , 1 ) ) ;
2018-12-10 11:57:12 +08:00
}
2018-12-10 13:04:41 +08:00
if ( e . FromModID = = ModHelper . Multiplayer . ModID & & e . Type = = MultiplayerSupport . FSTRING_SendBirthdayInfoToOthers )
2018-12-10 11:57:12 +08:00
{
2018-12-30 18:00:05 +08:00
KeyValuePair < long , PlayerData > message = e . ReadAs < KeyValuePair < long , PlayerData > > ( ) ;
2018-12-10 13:04:41 +08:00
if ( ! this . othersBirthdays . ContainsKey ( message . Key ) )
{
2018-12-30 18:00:05 +08:00
this . othersBirthdays . Add ( message . Key , message . Value ) ;
2018-12-10 13:04:41 +08:00
MultiplayerSupport . SendBirthdayInfoToConnectingPlayer ( e . FromPlayerID ) ;
2019-04-12 08:25:54 +08:00
this . Monitor . Log ( "Got other player's birthday data from: " + Game1 . getFarmer ( e . FromPlayerID ) . Name ) ;
2018-12-10 13:04:41 +08:00
}
else
{
//Brute force update birthday info if it has already been recevived but dont send birthday info again.
this . othersBirthdays . Remove ( message . Key ) ;
this . othersBirthdays . Add ( message . Key , message . Value ) ;
2019-04-12 08:25:54 +08:00
this . Monitor . Log ( "Got other player's birthday data from: " + Game1 . getFarmer ( e . FromPlayerID ) . Name ) ;
2018-12-10 13:04:41 +08:00
}
2018-12-10 11:57:12 +08:00
}
2018-09-19 09:04:38 +08:00
}
2019-01-06 15:21:06 +08:00
/// <summary>Raised after drawing the HUD (item toolbar, clock, etc) to the sprite batch, but before it's rendered to the screen.</summary>
2018-12-30 18:00:05 +08:00
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
2019-01-06 15:21:06 +08:00
private void OnRenderedHud ( object sender , RenderedHudEventArgs e )
2018-12-07 07:06:26 +08:00
{
2019-01-06 15:21:06 +08:00
if ( Game1 . activeClickableMenu = = null | | this . PlayerData ? . BirthdaySeason ? . ToLower ( ) ! = Game1 . currentSeason . ToLower ( ) )
return ;
2018-12-07 07:06:26 +08:00
2019-01-06 15:21:06 +08:00
if ( Game1 . activeClickableMenu is Billboard billboard )
2018-09-19 09:04:38 +08:00
{
2019-01-06 15:21:06 +08:00
if ( this . isDailyQuestBoard | | billboard . calendarDays = = null )
return ;
2018-12-07 07:06:26 +08:00
2018-09-19 09:04:38 +08:00
//Game1.player.FarmerRenderer.drawMiniPortrat(Game1.spriteBatch, new Vector2(Game1.activeClickableMenu.xPositionOnScreen + 152 + (index - 1) % 7 * 32 * 4, Game1.activeClickableMenu.yPositionOnScreen + 230 + (index - 1) / 7 * 32 * 4), 1f, 4f, 2, Game1.player);
string hoverText = "" ;
2018-12-06 16:54:12 +08:00
List < string > texts = new List < string > ( ) ;
2018-12-07 07:06:26 +08:00
2019-01-06 15:21:06 +08:00
foreach ( var clicky in billboard . calendarDays )
2018-09-19 09:04:38 +08:00
{
if ( clicky . containsPoint ( Game1 . getMouseX ( ) , Game1 . getMouseY ( ) ) )
{
2018-12-30 18:00:05 +08:00
if ( ! string . IsNullOrEmpty ( clicky . hoverText ) )
2018-12-06 16:54:12 +08:00
texts . Add ( clicky . hoverText ) ; //catches npc birhday names.
2018-12-30 18:00:05 +08:00
else if ( ! string . IsNullOrEmpty ( clicky . name ) )
2018-12-06 16:54:12 +08:00
texts . Add ( clicky . name ) ; //catches festival dates.
2018-09-19 09:04:38 +08:00
}
2018-12-06 16:54:12 +08:00
}
2018-12-30 18:00:05 +08:00
for ( int i = 0 ; i < texts . Count ; i + + )
{
2018-12-06 16:54:12 +08:00
hoverText + = texts [ i ] ; //Append text.
2018-12-30 18:00:05 +08:00
if ( i ! = texts . Count - 1 )
2018-12-06 16:54:12 +08:00
hoverText + = Environment . NewLine ; //Append new line.
2018-09-19 09:04:38 +08:00
}
2018-12-06 16:54:12 +08:00
2018-12-30 18:00:05 +08:00
if ( ! string . IsNullOrEmpty ( hoverText ) )
2018-09-19 09:04:38 +08:00
{
2018-12-30 18:00:05 +08:00
var oldText = this . Helper . Reflection . GetField < string > ( Game1 . activeClickableMenu , "hoverText" ) ;
2018-09-19 09:04:38 +08:00
oldText . SetValue ( hoverText ) ;
}
}
}
2019-01-06 15:21:06 +08:00
/// <summary>When a menu is open (<see cref="Game1.activeClickableMenu"/> isn't null), raised after that menu is drawn to the sprite batch but before it's rendered to the screen.</summary>
2018-12-30 18:00:05 +08:00
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
2019-01-06 15:21:06 +08:00
private void OnRenderedActiveMenu ( object sender , RenderedActiveMenuEventArgs e )
2018-09-19 09:04:38 +08:00
{
2018-12-30 18:00:05 +08:00
if ( Game1 . activeClickableMenu = = null | | this . isDailyQuestBoard )
return ;
2018-12-10 13:04:41 +08:00
2018-12-30 18:00:05 +08:00
//Don't do anything if birthday has not been chosen yet.
if ( this . PlayerData = = null )
return ;
2018-12-06 10:06:11 +08:00
2018-09-19 09:04:38 +08:00
if ( Game1 . activeClickableMenu is Billboard )
{
2018-12-30 18:00:05 +08:00
if ( ! string . IsNullOrEmpty ( this . PlayerData . BirthdaySeason ) )
2018-12-10 13:04:41 +08:00
{
2018-12-30 18:00:05 +08:00
if ( this . PlayerData . BirthdaySeason . ToLower ( ) = = Game1 . currentSeason . ToLower ( ) )
2018-12-10 13:04:41 +08:00
{
2018-12-30 18:00:05 +08:00
int index = this . PlayerData . BirthdayDay ;
2018-12-10 13:04:41 +08:00
Game1 . player . FarmerRenderer . drawMiniPortrat ( Game1 . spriteBatch , new Vector2 ( Game1 . activeClickableMenu . xPositionOnScreen + 152 + ( index - 1 ) % 7 * 32 * 4 , Game1 . activeClickableMenu . yPositionOnScreen + 230 + ( index - 1 ) / 7 * 32 * 4 ) , 0.5f , 4f , 2 , Game1 . player ) ;
2019-04-12 08:25:54 +08:00
( Game1 . activeClickableMenu as Billboard ) . drawMouse ( e . SpriteBatch ) ;
string hoverText = this . Helper . Reflection . GetField < string > ( ( Game1 . activeClickableMenu as Billboard ) , "hoverText" , true ) . GetValue ( ) ;
if ( hoverText . Length > 0 )
{
IClickableMenu . drawHoverText ( Game1 . spriteBatch , hoverText , Game1 . dialogueFont , 0 , 0 , - 1 , ( string ) null , - 1 , ( string [ ] ) null , ( Item ) null , 0 , - 1 , - 1 , - 1 , - 1 , 1f , ( CraftingRecipe ) null ) ;
}
2018-12-10 13:04:41 +08:00
}
}
2018-12-30 18:00:05 +08:00
foreach ( var pair in this . othersBirthdays )
2018-12-10 13:04:41 +08:00
{
int index = pair . Value . BirthdayDay ;
if ( pair . Value . BirthdaySeason ! = Game1 . currentSeason . ToLower ( ) ) continue ; //Hide out of season birthdays.
index = pair . Value . BirthdayDay ;
Game1 . player . FarmerRenderer . drawMiniPortrat ( Game1 . spriteBatch , new Vector2 ( Game1 . activeClickableMenu . xPositionOnScreen + 152 + ( index - 1 ) % 7 * 32 * 4 , Game1 . activeClickableMenu . yPositionOnScreen + 230 + ( index - 1 ) / 7 * 32 * 4 ) , 0.5f , 4f , 2 , Game1 . getFarmer ( pair . Key ) ) ;
2019-04-12 08:25:54 +08:00
( Game1 . activeClickableMenu as Billboard ) . drawMouse ( e . SpriteBatch ) ;
string hoverText = this . Helper . Reflection . GetField < string > ( ( Game1 . activeClickableMenu as Billboard ) , "hoverText" , true ) . GetValue ( ) ;
if ( hoverText . Length > 0 )
{
IClickableMenu . drawHoverText ( Game1 . spriteBatch , hoverText , Game1 . dialogueFont , 0 , 0 , - 1 , ( string ) null , - 1 , ( string [ ] ) null , ( Item ) null , 0 , - 1 , - 1 , - 1 , - 1 , 1f , ( CraftingRecipe ) null ) ;
}
2018-12-10 13:04:41 +08:00
}
2019-07-08 08:09:28 +08:00
( Game1 . activeClickableMenu ) . drawMouse ( e . SpriteBatch ) ;
2018-09-19 09:04:38 +08:00
}
}
2019-01-06 15:21:06 +08:00
/// <summary>Raised after a game menu is opened, closed, or replaced.</summary>
2018-12-30 18:00:05 +08:00
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
2019-01-06 15:21:06 +08:00
private void OnMenuChanged ( object sender , MenuChangedEventArgs e )
2018-09-19 09:04:38 +08:00
{
2019-01-06 15:21:06 +08:00
switch ( e . NewMenu )
2018-12-07 07:06:26 +08:00
{
2019-01-06 15:21:06 +08:00
case null :
this . isDailyQuestBoard = false ;
2019-07-08 08:09:28 +08:00
//Validate the gift and give it to the player.
if ( this . lastSpeaker ! = null )
{
if ( this . giftManager . BirthdayGiftToReceive ! = null & & this . VillagerQueue [ this . lastSpeaker . Name ] . hasGivenBirthdayGift = = false )
{
while ( this . giftManager . BirthdayGiftToReceive . Name = = "Error Item" | | this . giftManager . BirthdayGiftToReceive . Name = = "Rock" | | this . giftManager . BirthdayGiftToReceive . Name = = "???" )
this . giftManager . SetNextBirthdayGift ( this . lastSpeaker . Name ) ;
Game1 . player . addItemByMenuIfNecessaryElseHoldUp ( this . giftManager . BirthdayGiftToReceive ) ;
this . giftManager . BirthdayGiftToReceive = null ;
this . VillagerQueue [ this . lastSpeaker . Name ] . hasGivenBirthdayGift = true ;
this . lastSpeaker = null ;
}
}
2019-01-06 15:21:06 +08:00
return ;
2018-12-10 13:04:41 +08:00
2019-01-06 15:21:06 +08:00
case Billboard billboard :
2018-12-10 13:04:41 +08:00
{
2019-01-06 15:21:06 +08:00
this . isDailyQuestBoard = ModHelper . Reflection . GetField < bool > ( ( Game1 . activeClickableMenu as Billboard ) , "dailyQuestBoard" , true ) . GetValue ( ) ;
if ( this . isDailyQuestBoard )
return ;
Texture2D text = new Texture2D ( Game1 . graphics . GraphicsDevice , 1 , 1 ) ;
Color [ ] col = new Color [ 1 ] ;
col [ 0 ] = new Color ( 0 , 0 , 0 , 1 ) ;
text . SetData < Color > ( col ) ;
//players birthday position rect=new ....
if ( ! string . IsNullOrEmpty ( this . PlayerData . BirthdaySeason ) )
{
if ( this . PlayerData . BirthdaySeason . ToLower ( ) = = Game1 . currentSeason . ToLower ( ) )
{
int index = this . PlayerData . BirthdayDay ;
2019-12-02 12:47:31 +08:00
string bdayDisplay = Game1 . content . LoadString ( "Strings\\UI:Billboard_Birthday" ) ;
2019-01-06 15:21:06 +08:00
Rectangle birthdayRect = new Rectangle ( Game1 . activeClickableMenu . xPositionOnScreen + 152 + ( index - 1 ) % 7 * 32 * 4 , Game1 . activeClickableMenu . yPositionOnScreen + 200 + ( index - 1 ) / 7 * 32 * 4 , 124 , 124 ) ;
2019-12-02 12:47:31 +08:00
billboard . calendarDays . Add ( new ClickableTextureComponent ( "" , birthdayRect , "" , string . Format ( bdayDisplay , Game1 . player . Name ) , text , new Rectangle ( 0 , 0 , 124 , 124 ) , 1f , false ) ) ;
//billboard.calendarDays.Add(new ClickableTextureComponent("", birthdayRect, "", $"{Game1.player.Name}'s Birthday", text, new Rectangle(0, 0, 124, 124), 1f, false));
2019-01-06 15:21:06 +08:00
}
}
foreach ( var pair in this . othersBirthdays )
{
if ( pair . Value . BirthdaySeason ! = Game1 . currentSeason . ToLower ( ) ) continue ;
int index = pair . Value . BirthdayDay ;
2019-12-02 12:47:31 +08:00
string bdayDisplay = Game1 . content . LoadString ( "Strings\\UI:Billboard_Birthday" ) ;
2019-01-06 15:21:06 +08:00
Rectangle otherBirthdayRect = new Rectangle ( Game1 . activeClickableMenu . xPositionOnScreen + 152 + ( index - 1 ) % 7 * 32 * 4 , Game1 . activeClickableMenu . yPositionOnScreen + 200 + ( index - 1 ) / 7 * 32 * 4 , 124 , 124 ) ;
2019-12-02 12:47:31 +08:00
billboard . calendarDays . Add ( new ClickableTextureComponent ( "" , otherBirthdayRect , "" , string . Format ( bdayDisplay , Game1 . getFarmer ( pair . Key ) . Name ) , text , new Rectangle ( 0 , 0 , 124 , 124 ) , 1f , false ) ) ;
2019-01-06 15:21:06 +08:00
}
2019-07-08 08:09:28 +08:00
break ;
}
case DialogueBox dBox :
{
if ( Game1 . eventUp ) return ;
//Hijack the dialogue box and ensure that birthday dialogue gets spoken.
if ( Game1 . currentSpeaker ! = null )
{
this . lastSpeaker = Game1 . currentSpeaker ;
if ( Game1 . activeClickableMenu ! = null & & this . IsBirthday ( ) & & this . VillagerQueue . ContainsKey ( Game1 . currentSpeaker . Name ) )
{
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 )
{
2019-12-03 11:10:56 +08:00
if ( Game1 . player . getSpouse ( ) . Name . Equals ( Game1 . currentSpeaker . Name ) )
2019-07-08 08:09:28 +08:00
{
2019-12-03 11:10:56 +08:00
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 @!" ;
}
}
2019-07-08 08:09:28 +08:00
}
else
{
2019-12-03 11:10:56 +08:00
if ( this . messages . birthdayWishes . ContainsKey ( Game1 . currentSpeaker . Name ) )
{
dialogueMessage = this . messages . birthdayWishes [ Game1 . currentSpeaker . Name ] ;
}
else
{
dialogueMessage = "Happy Birthday @!" ;
}
2019-07-08 08:09:28 +08:00
}
}
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 ) )
{
2019-12-02 12:47:31 +08:00
if ( string . IsNullOrEmpty ( this . messages . spouseBirthdayWishes [ Game1 . currentSpeaker . Name ] ) = = false ) {
d = new Dialogue ( this . messages . spouseBirthdayWishes [ Game1 . currentSpeaker . Name ] , Game1 . currentSpeaker ) ;
}
else
{
2019-12-03 11:10:56 +08:00
d = new Dialogue ( this . messages . generateSpouseBirthdayDialogue ( Game1 . currentSpeaker . Name ) , Game1 . currentSpeaker ) ;
2019-12-02 12:47:31 +08:00
}
2019-07-08 08:09:28 +08:00
}
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 ) ;
}
}
Game1 . currentSpeaker . resetCurrentDialogue ( ) ;
Game1 . currentSpeaker . resetSeasonalDialogue ( ) ;
this . Helper . Reflection . GetMethod ( Game1 . currentSpeaker , "loadCurrentDialogue" , true ) . Invoke ( ) ;
Game1 . npcDialogues [ Game1 . currentSpeaker . Name ] = Game1 . currentSpeaker . CurrentDialogue ;
if ( this . IsBirthday ( ) & & this . VillagerQueue [ Game1 . currentSpeaker . Name ] . hasGivenBirthdayGift = = false & & Game1 . player . getFriendshipHeartLevelForNPC ( Game1 . currentSpeaker . Name ) > = Config . minNeutralFriendshipGiftLevel )
{
try
{
this . giftManager . SetNextBirthdayGift ( Game1 . currentSpeaker . Name ) ;
this . Monitor . Log ( "Setting next birthday gift. 1" ) ;
}
catch ( Exception ex )
{
this . Monitor . Log ( ex . ToString ( ) , LogLevel . Error ) ;
}
}
Game1 . activeClickableMenu = new DialogueBox ( d ) ;
this . VillagerQueue [ Game1 . currentSpeaker . Name ] . hasGivenBirthdayWish = true ;
// Set birthday gift for the player to recieve from the npc they are currently talking with.
}
}
}
2019-01-06 15:21:06 +08:00
break ;
2018-12-10 13:04:41 +08:00
}
2018-09-19 09:04:38 +08:00
}
2019-07-08 08:09:28 +08:00
2017-02-22 15:29:00 +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-30 06:02:49 +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-10-20 15:01:51 +08:00
{
2019-07-08 08:09:28 +08:00
try
{
this . ResetVillagerQueue ( ) ;
}
catch ( Exception ex )
{
this . Monitor . Log ( ex . ToString ( ) , LogLevel . Error ) ;
}
2017-07-30 06:02:49 +08:00
this . CheckedForBirthday = false ;
2016-10-20 15:01:51 +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-30 06:02:49 +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-10-20 15:01:51 +08:00
{
2017-07-30 06:02:49 +08:00
// show birthday selection menu
2018-06-26 12:13:40 +08:00
if ( Game1 . activeClickableMenu ! = null ) return ;
2019-01-06 15:21:06 +08:00
if ( Context . IsPlayerFree & & ! this . HasChosenBirthday & & e . Button = = Config . KeyBinding )
2017-08-06 03:51:44 +08:00
Game1 . activeClickableMenu = new BirthdayMenu ( this . PlayerData . BirthdaySeason , this . PlayerData . BirthdayDay , this . SetBirthday ) ;
2016-10-20 15:01:51 +08:00
}
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-30 06:02:49 +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 )
2016-10-20 15:01:51 +08:00
{
2019-01-06 15:21:06 +08:00
this . DataFilePath = Path . Combine ( "data" , $"{Game1.player.Name}_{Game1.player.UniqueMultiplayerID}.json" ) ;
2018-05-10 05:23:42 +08:00
2017-08-06 11:00:37 +08:00
// reset state
2019-07-08 08:09:28 +08:00
this . VillagerQueue = new Dictionary < string , VillagerInfo > ( ) ;
2017-08-06 11:00:37 +08:00
this . CheckedForBirthday = false ;
// load settings
2017-08-06 03:51:44 +08:00
this . MigrateLegacyData ( ) ;
2018-12-06 09:16:28 +08:00
this . PlayerData = this . Helper . Data . ReadJsonFile < PlayerData > ( this . DataFilePath ) ? ? new PlayerData ( ) ;
2018-09-19 09:04:38 +08:00
2018-12-22 07:28:52 +08:00
;
2018-12-10 13:04:41 +08:00
if ( PlayerBirthdayData ! = null )
{
2019-04-12 08:25:54 +08:00
ModMonitor . Log ( "Send all birthday information from " + Game1 . player . Name ) ;
2018-12-10 13:04:41 +08:00
MultiplayerSupport . SendBirthdayInfoToOtherPlayers ( ) ;
}
2017-07-30 06:02:49 +08:00
//this.SeenEvent = false;
//this.Dialogue = new Dictionary<string, Dialogue>();
2019-12-03 11:10:56 +08:00
//Game1.player.addItemToInventoryBool(new StardewValley.Object(388, 999));
//Game1.player.addItemToInventoryBool(new StardewValley.Object(390, 999));
//Game1.player.Money = 999999;
2016-10-20 15:01:51 +08:00
}
2019-01-06 15:21:06 +08:00
/// <summary>Raised before the game begins writes data to the save file (except the initial save creation).</summary>
2017-08-06 03:51:44 +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 OnSaving ( object sender , SavingEventArgs e )
2017-08-06 03:51:44 +08:00
{
if ( this . HasChosenBirthday )
2018-12-06 09:16:28 +08:00
this . Helper . Data . WriteJsonFile ( this . DataFilePath , this . PlayerData ) ;
2017-08-06 03:51:44 +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-30 06:02:49 +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-10-20 15:01:51 +08:00
{
2019-04-12 08:25:54 +08:00
2017-08-06 03:23:10 +08:00
if ( ! Context . IsWorldReady | | Game1 . eventUp | | Game1 . isFestival ( ) )
2017-07-30 06:02:49 +08:00
return ;
2019-07-08 08:09:28 +08:00
2018-12-30 18:00:05 +08:00
if ( ! this . HasChosenBirthday & & Game1 . activeClickableMenu = = null & & Game1 . player . Name . ToLower ( ) ! = "unnamed farmhand" )
2018-08-06 00:33:00 +08:00
{
Game1 . activeClickableMenu = new BirthdayMenu ( this . PlayerData . BirthdaySeason , this . PlayerData . BirthdayDay , this . SetBirthday ) ;
this . CheckedForBirthday = false ;
}
2017-02-22 15:29:00 +08:00
2018-12-30 18:00:05 +08:00
if ( ! this . CheckedForBirthday & & Game1 . activeClickableMenu = = null )
2016-10-20 15:01:51 +08:00
{
2017-07-30 06:02:49 +08:00
this . CheckedForBirthday = true ;
// set up birthday
if ( this . IsBirthday ( ) )
2016-10-20 15:01:51 +08:00
{
2019-01-15 04:26:36 +08:00
string starMessage = BirthdayMessages . GetTranslatedString ( "Happy Birthday: Star Message" ) ;
2019-04-12 08:25:54 +08:00
ModMonitor . Log ( starMessage ) ;
2019-01-15 04:26:36 +08:00
Messages . ShowStarMessage ( starMessage ) ;
2018-12-10 11:57:12 +08:00
MultiplayerSupport . SendBirthdayMessageToOtherPlayers ( ) ;
2019-04-12 08:25:54 +08:00
2018-05-01 09:21:31 +08:00
Game1 . player . mailbox . Add ( "birthdayMom" ) ;
Game1 . player . mailbox . Add ( "birthdayDad" ) ;
2017-02-22 15:29:00 +08:00
2019-07-08 08:09:28 +08:00
2017-07-30 06:02:49 +08:00
foreach ( GameLocation location in Game1 . locations )
2016-10-20 15:01:51 +08:00
{
foreach ( NPC npc in location . characters )
{
2017-07-30 06:02:49 +08:00
if ( npc is Child | | npc is Horse | | npc is Junimo | | npc is Monster | | npc is Pet )
continue ;
2018-12-07 08:13:41 +08:00
//Add in birthday dialogues for npc.
2016-10-20 15:01:51 +08:00
try
{
2018-09-19 09:04:38 +08:00
if ( Game1 . player . getFriendshipHeartLevelForNPC ( npc . Name ) > = Config . minimumFriendshipLevelForBirthdayWish )
{
2018-12-07 08:13:41 +08:00
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.
2018-12-30 18:00:05 +08:00
if ( this . messages . spouseBirthdayWishes . ContainsKey ( npc . Name ) & & Game1 . player . isMarried ( ) )
2018-12-07 08:13:41 +08:00
{
2019-12-03 11:10:56 +08:00
if ( Game1 . player . getSpouse ( ) . Name . Equals ( npc . Name ) )
2018-12-07 08:13:41 +08:00
{
2019-12-03 11:10:56 +08:00
//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 ) ;
}
2018-12-07 08:13:41 +08:00
}
2019-12-03 11:10:56 +08:00
2018-12-07 08:13:41 +08:00
}
2018-12-30 18:00:05 +08:00
if ( ! spouseMessage )
2018-12-07 08:13:41 +08:00
{
//Load in
2018-12-30 18:00:05 +08:00
Dialogue d = new Dialogue ( this . messages . birthdayWishes [ npc . Name ] , npc ) ;
2018-12-07 08:13:41 +08:00
npc . CurrentDialogue . Push ( d ) ;
2019-07-08 08:09:28 +08:00
if ( npc . CurrentDialogue . Peek ( ) ! = d ) npc . setNewDialogue ( this . messages . birthdayWishes [ npc . Name ] ) ;
2018-12-07 08:13:41 +08:00
}
2018-09-19 09:04:38 +08:00
}
2016-10-20 15:01:51 +08:00
}
catch
{
2018-09-19 09:04:38 +08:00
if ( Game1 . player . getFriendshipHeartLevelForNPC ( npc . Name ) > = Config . minimumFriendshipLevelForBirthdayWish )
{
Dialogue d = new Dialogue ( "Happy Birthday @!" , npc ) ;
npc . CurrentDialogue . Push ( d ) ;
2019-07-08 08:09:28 +08:00
if ( npc . CurrentDialogue . Peek ( ) ! = d )
2018-09-19 09:04:38 +08:00
npc . setNewDialogue ( "Happy Birthday @!" ) ;
}
2016-10-20 15:01:51 +08:00
}
}
}
}
2016-11-08 17:19:56 +08:00
2018-12-06 09:16:28 +08:00
//Don't constantly set the birthday menu.
2018-12-30 18:00:05 +08:00
if ( Game1 . activeClickableMenu ? . GetType ( ) = = typeof ( BirthdayMenu ) )
return ;
2017-07-30 06:02:49 +08:00
// ask for birthday date
2018-12-30 18:00:05 +08:00
if ( ! this . HasChosenBirthday & & Game1 . activeClickableMenu = = null )
2016-11-08 17:19:56 +08:00
{
2017-08-06 03:51:44 +08:00
Game1 . activeClickableMenu = new BirthdayMenu ( this . PlayerData . BirthdaySeason , this . PlayerData . BirthdayDay , this . SetBirthday ) ;
2017-07-30 06:02:49 +08:00
this . CheckedForBirthday = false ;
2016-11-08 17:19:56 +08:00
}
}
2018-12-06 09:16:28 +08:00
2016-10-20 15:01:51 +08:00
}
2018-12-06 09:16:28 +08:00
/// <summary>Set the player's birthday/</summary>
2017-07-30 06:02:49 +08:00
/// <param name="season">The birthday season.</param>
/// <param name="day">The birthday day.</param>
private void SetBirthday ( string season , int day )
2016-10-20 15:01:51 +08:00
{
2017-08-06 03:51:44 +08:00
this . PlayerData . BirthdaySeason = season ;
this . PlayerData . BirthdayDay = day ;
2017-02-22 15:29:00 +08:00
}
2017-07-30 06:02:49 +08:00
/// <summary>Reset the queue of villager names.</summary>
private void ResetVillagerQueue ( )
2017-02-22 15:29:00 +08:00
{
2017-07-30 06:02:49 +08:00
this . VillagerQueue . Clear ( ) ;
foreach ( GameLocation location in Game1 . locations )
2016-10-20 15:01:51 +08:00
{
2017-07-30 06:02:49 +08:00
foreach ( NPC npc in location . characters )
2016-10-20 15:01:51 +08:00
{
2017-07-30 06:02:49 +08:00
if ( npc is Child | | npc is Horse | | npc is Junimo | | npc is Monster | | npc is Pet )
2017-02-22 15:29:00 +08:00
continue ;
2019-07-08 08:09:28 +08:00
if ( this . VillagerQueue . ContainsKey ( npc . Name ) )
2017-07-30 06:02:49 +08:00
continue ;
2019-07-08 08:09:28 +08:00
this . VillagerQueue . Add ( npc . Name , new VillagerInfo ( ) ) ;
2017-02-22 15:29:00 +08:00
}
2016-10-20 15:01:51 +08:00
}
}
2017-07-30 06:02:49 +08:00
/// <summary>Get whether today is the player's birthday.</summary>
private bool IsBirthday ( )
{
return
2017-08-06 03:51:44 +08:00
this . PlayerData . BirthdayDay = = Game1 . dayOfMonth
2019-12-03 12:18:43 +08:00
& & this . PlayerData . BirthdaySeason . Equals ( HappyBirthday . Config . translationInfo . getTranslatedString ( Game1 . currentSeason ) ) ;
2017-07-30 06:02:49 +08:00
}
2016-10-20 15:01:51 +08:00
2017-08-06 03:51:44 +08:00
/// <summary>Migrate the legacy settings for the current player.</summary>
private void MigrateLegacyData ( )
2017-07-30 06:02:49 +08:00
{
2017-08-06 03:51:44 +08:00
// skip if no legacy data or new data already exists
try
2016-10-20 15:01:51 +08:00
{
2018-01-31 04:46:04 +08:00
if ( ! File . Exists ( this . LegacyDataFilePath ) | | File . Exists ( this . DataFilePath ) )
2018-12-30 18:00:05 +08:00
{
if ( this . PlayerData = = null )
this . PlayerData = new PlayerData ( ) ;
}
2016-10-20 15:01:51 +08:00
}
2018-12-30 18:00:05 +08:00
catch
2016-10-20 15:01:51 +08:00
{
2018-01-31 04:46:04 +08:00
// migrate to new file
try
{
string [ ] text = File . ReadAllLines ( this . LegacyDataFilePath ) ;
2018-12-06 09:16:28 +08:00
this . Helper . Data . WriteJsonFile ( this . DataFilePath , new PlayerData
2018-01-31 04:46:04 +08:00
{
BirthdaySeason = text [ 3 ] ,
BirthdayDay = Convert . ToInt32 ( text [ 5 ] )
} ) ;
FileInfo file = new FileInfo ( this . LegacyDataFilePath ) ;
file . Delete ( ) ;
if ( ! file . Directory . EnumerateFiles ( ) . Any ( ) )
file . Directory . Delete ( ) ;
}
catch ( Exception ex )
{
this . Monitor . Log ( $"Error migrating data from the legacy 'Player_Birthdays' folder for the current player. Technical details:\n {ex}" , LogLevel . Error ) ;
}
2016-10-20 15:01:51 +08:00
}
}
}
}