Modding Help [SMAPI] How do I call on the current time in the game to trigger something?

Discussion in 'Mods' started by Gwenhwyfar, Jul 24, 2016.

  1. Gwenhwyfar

    Gwenhwyfar Subatomic Cosmonaut

    I'm finally dipping my hands into SMAPI modding.

    I've decided to try making my own action code, using Advize's template code for that.

    I'm looking at the different packages for the references, StardewValley and StardewValleyAPI, and have only determined that I don't know what a good chunk of them do.

    Here's what I want to accomplish:

    Action NewGunther:
    1. If currentTime < 8 AM, trigger "Action Message The Archaeology House Counter is open between 8 AM and 6 PM. Please come back later. - Gunther"
    2. Else if 1 PM < currentTime < 2 PM, trigger "Action Message On lunch break! Be back in an hour. - Gunther"
    3. Else if 6 PM < currentTime, trigger "Action Message The Archaeology House Counter is open between 8 AM and 6 PM. Please come back later. - Gunther"
    4. Else trigger "Action Gunther"

    If I can't call on previous actions already made in the game, this is how I would do it:
    1. If currentTime < 8 AM, create message "The Archaeology House Counter is open between 8 AM and 6 PM. Please come back later. - Gunther"
    2. Else if 1 PM < currentTime < 2 PM, create message "On lunch break! Be back in an hour. - Gunther"
    3. Else if 6 PM < currentTime, create message "The Archaeology House Counter is open between 8 AM and 6 PM. Please come back later. - Gunther"
    4. Else
      1. If character has new fossil, create normal Gunther dialogue for new fossil and open museum menu.
      2. Else character has no new fossil, create normal Gunther dialogue saying you have no new fossils.


    Action Gunther is the normal dialogue you currently get from Gunther when you click on the counter in front of him. I wanted to modify the times for the action to occur and when it's not open, so Gunther can have breaks.

    How could I call on the time to do this? Might need more help in the future with this, but for right now, I want help with figuring out how to call the time and try to figure out the rest on my own.
     
    • Jinxiewinxie

      Jinxiewinxie Farmer Fashionista

      Code:
      
      public override void Entry(params object[] objects)
      {
          TimeEvents.TimeOfDayChanged += Event_TimeOfDayChanged;
      }
      
      private void TimeEvents_TimeOfDayChanged(object sender, EventArgsIntChanged e)
      {
          if (Game1.timeOfDay == 800)
           {
           //do stuff
           }
      }
      
       
      • Gwenhwyfar

        Gwenhwyfar Subatomic Cosmonaut

        Thanks Jinxie!

        Now I'm gonna see if it's possible to call on the old Gunther action so I don't have to attempt rewriting it from scratch... Or screw up the action...
        --- Post updated ---
        Okay, so I made the mod and I want to test it out. Is there a certain way I need to package it to get it to work?

        What do I do?

        Edit: Ugh... This post update glitch again... Why do I keep using it? >.<

        Edit:

        Lol... So I put up the .dll file with the manifest and my mod doesn't do anything.. I try to click on the tile but it doesn't trigger anything... I see a hand hovering over it though... Hmm...

        Using Advize's action template code, here's the code I tried to do:
        Code:
        using StardewModdingAPI;
        using StardewModdingAPI.Events;
        using StardewValley;
        using Microsoft.Xna.Framework.Input;
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks;
        
        namespace New_Gunther_Action
        {
            public class Class1: Mod
            {
                static void Events_MouseChanged(object sender, EventArgsMouseStateChanged e)
                {
                    if (!Game1.hasLoadedGame) return;
                    if (e.NewState.RightButton == ButtonState.Pressed && e.PriorState.RightButton != ButtonState.Pressed)
                    {
                        CheckForAction();
                    }
                }
                public override void Entry(params object[] objects)
                {
                    ControlEvents.MouseChanged += Events_MouseChanged;
                    ControlEvents.ControllerButtonPressed += Events_ControllerButtonPressed;
                    TimeEvents.TimeOfDayChanged += Event_TimeOfDayChanged;
                }
        
                static void Events_ControllerButtonPressed(object sender, EventArgsControllerButtonPressed e)
                {
                    if (!Game1.hasLoadedGame) return;
                    if (e.ButtonPressed == Buttons.A)
                    {
                        CheckForAction();
                    }
                }
        
                static void CheckForAction()
                {
                    if (!Game1.player.UsingTool && !Game1.pickingTool && !Game1.menuUp && (!Game1.eventUp || Game1.currentLocation.currentEvent.playerControlSequence) && !Game1.nameSelectUp && Game1.numberOfSelectedItems == -1 && !Game1.fadeToBlack)
                    {
                        Vector2 grabTile = new Vector2((float)(Game1.getOldMouseX() + Game1.viewport.X), (float)(Game1.getOldMouseY() + Game1.viewport.Y)) / (float)Game1.tileSize;
                        if (!Utility.tileWithinRadiusOfPlayer((int)grabTile.X, (int)grabTile.Y, 1, Game1.player))
                        {
                            grabTile = Game1.player.GetGrabTile();
                        }
                        xTile.Tiles.Tile tile = Game1.currentLocation.map.GetLayer("Buildings").PickTile(new xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize), Game1.viewport.Size);
                        xTile.ObjectModel.PropertyValue propertyValue = null;
                        if (tile != null)
                        {
                            tile.Properties.TryGetValue("Action", out propertyValue);
                        }
                        if (propertyValue != null)
                        {
                            if (propertyValue == "NewGunther") //Change this to your Action property
                            {
                                if (Game1.timeOfDay < 800)
                                {
                                    DialogueBox("The Archaeology House Counter is open between 8 AM and 6 PM. Please come back another time. - Gunther");
                                }
                                else if (1230 <= Game1.timeOfDay < 1400)
                                {
                                    DialogueBox("On lunch break! Be back at 2 PM. - Gunther");
                                }
                                else if (Game1.timeOfDay >= 1800)
                                {
                                    DialogueBox("The Archaeology House Counter is open between 8 AM and 6 PM. Please come back another time. - Gunther");
                                }
                                else
                                {
                                    tile.Properties.TryGetValue("Action", out "Gunther");
                                }
                            };
                        }
                    }
                }
            }
        }
         
          Last edited: Jul 24, 2016
        • Jinxiewinxie

          Jinxiewinxie Farmer Fashionista

          Hmmm might need Advize or Entoarox to help with this one past this =/
           
          • Gwenhwyfar

            Gwenhwyfar Subatomic Cosmonaut

            I thought dialoguebox would work for bringing up a message, but I guess not? Could it be an issue with how I executed the current time?

            Maybe trying to call on the old Gunther action doesn't work...
            --- Post updated ---
            @Advize and @Entoarox could you guys give me pointers in my coding?
             
            • Jinxiewinxie

              Jinxiewinxie Farmer Fashionista

              Maybe you need to use a different line? In WFL, I use this:

              Code:
              Game1.drawObjectDialogue("Words Go Here");
              --- Post updated ---
              This works, its done on the farm because I'm lazy, but hopefully it helps:

              Code:
              using StardewModdingAPI;
              using StardewModdingAPI.Events;
              using StardewValley;
              using Microsoft.Xna.Framework.Input;
              using System;
              using Microsoft.Xna.Framework;
              using xTile.Dimensions;
              
              namespace Gunther
              {
                  public class Gunther : Mod
                  {
              
                      public static string GuntherMessage = "";
              
                      public override void Entry(params object[] objects)
                      {
                          ControlEvents.MouseChanged += Events_MouseChanged;
                          ControlEvents.ControllerButtonPressed += Events_ControllerButtonPressed;
                          TimeEvents.TimeOfDayChanged += Event_TimeOfDayChanged;
                          GameEvents.UpdateTick += Event_UpdateTick;
                      }
              
                      static void Events_MouseChanged(object sender, EventArgsMouseStateChanged e)
                      {
                          if (!Game1.hasLoadedGame) return;
                          if (e.NewState.RightButton == ButtonState.Pressed && e.PriorState.RightButton != ButtonState.Pressed)
                          {
                              CheckForAction();
                          }
                          if (e.NewState.LeftButton == ButtonState.Pressed && e.PriorState.LeftButton != ButtonState.Pressed)
                          {
                              CheckForAction();
                          }
                      }
                  
                      private void Event_UpdateTick(object sender, EventArgs e)
                      {
                          if (!Game1.hasLoadedGame) return;
                          if (!(Game1.currentLocation == Game1.getLocationFromName("Farm"))) return;
              
                          Game1.getFarm().setTileProperty(77, 17, "Buildings", "Action", "NewGunther");
                          Game1.getFarm().setTileProperty(77, 19, "Buildings", "Action", "NewGunther");
              
                          GameEvents.UpdateTick -= Event_UpdateTick;
                      }
              
                      private void Event_TimeOfDayChanged(object sender, EventArgsIntChanged e)
                      {
                          if (Game1.timeOfDay < 800 || Game1.timeOfDay > 1800)
                          {
                              GuntherMessage = "The Archeology House Counter is open between 8AM and 6PM. Please come back another time. - Gunther";
                          }
                          if (Game1.timeOfDay > 800 && Game1.timeOfDay < 1230)
                          {
                              GuntherMessage = null;
                          }
                          if (Game1.timeOfDay > 1230 && Game1.timeOfDay < 1400)
                          {
                              GuntherMessage = "On luch break! Be back at 2PM. - Gunther";
                          }
                          if (Game1.timeOfDay > 1400 && Game1.timeOfDay < 1800)
                          {
                              GuntherMessage = null;
                          }
                      }
              
                      static void Events_ControllerButtonPressed(object sender, EventArgsControllerButtonPressed e)
                      {
                          if (!Game1.hasLoadedGame) return;
                          if (e.ButtonPressed == Buttons.A)
                          {
                              CheckForAction();
                          }
                      }
              
                      static void CheckForAction()
                      {
                          if (!Game1.player.UsingTool && !Game1.pickingTool && !Game1.menuUp && (!Game1.eventUp || Game1.currentLocation.currentEvent.playerControlSequence) && !Game1.nameSelectUp && Game1.numberOfSelectedItems == -1 && !Game1.fadeToBlack && Game1.activeClickableMenu == null)
                          {
                              Vector2 grabTile = new Vector2(Game1.getOldMouseX() + Game1.viewport.X, Game1.getOldMouseY() + Game1.viewport.Y) / Game1.tileSize;
                              if (!Utility.tileWithinRadiusOfPlayer((int)grabTile.X, (int)grabTile.Y, 1, Game1.player))
                              {
                                  grabTile = Game1.player.GetGrabTile();
                              }
                              xTile.Tiles.Tile tile = Game1.currentLocation.map.GetLayer("Buildings").PickTile(new Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize), Game1.viewport.Size);
                              xTile.ObjectModel.PropertyValue propertyValue = null;
                              if (tile != null)
                              {
                                  tile.Properties.TryGetValue("Action", out propertyValue);
                              }
                              if (propertyValue != null)
                              {
                                  if (propertyValue == "NewGunther")
                                  {
                                      Game1.drawObjectDialogue(GuntherMessage);
                                  
                                  }
                              }
                          }
                      }
                  }
              }
              
              It adds a property value to this tile, ish:

              upload_2016-7-24_10-36-16.png

              I only tested before 8am, between 8 and lunch, and then during lunch. You probably need to add another timeslot for the message to be null between lunch and close, so that you don't get a message during those times.
               

                Attached Files:

              • Entoarox

                Entoarox Oxygen Tank

                Things I noticed:
                1) I dont see you replace the tile action anywhere, are you doing that somewhere else?
                2) Your else does not work, you need to directly invoke the relevant code since there is no way to trigger the "gunther" action indirectly.
                 
                • Gwenhwyfar

                  Gwenhwyfar Subatomic Cosmonaut

                  @Entoarox Well, I was putting in the NewGunther action in tIDE. So I will have to program it in instead, especially since I'm trying to trigger the original "Gunther" action indirectly?

                  Edit:

                  @Jinxiewinxie Thanks! I'll test out your suggestion with the dialogue box.
                   
                  • Entoarox

                    Entoarox Oxygen Tank

                    You cant trigger a action indirectly the way you are trying, it just doesnt work like that :p
                     
                    • Gwenhwyfar

                      Gwenhwyfar Subatomic Cosmonaut

                      :p Oh! Well, I was just trying to make it cheap and simple, since I want to be lazy and not attempt to create the entire Gunther action from scratch with added modifications. I wanted people to be able to easily include the NewGunther action, if they wanted, to another tile, if they had any reason to. I'm also afraid of forgetting something or not doing it right, like if you get rewarded by Gunther for a certain amount of artifacts through the mail or talking to him at the counter. I actually haven't gone far in the vanilla game, not even far enough to reach my first museum reward, before deciding that I wanted to mod the game. I would want to be articulate and provide the exact same dialogue Gunther gives if you have something to donate to the museum, you have nothing to donate, or you get a reward, lol... >.>; I'm too much of a perfectionist like that.... Because of that, I'm kind of afraid I'll annoy people I work together in making a mod I envision because I'll be too articulate and want them to do it exactly as I envision....

                      I'll rewrite my code and include the location for where this tile property gets triggered. So how could I trigger the Gunther action then? Will I have to entirely rewrite it, or is it still possible to indirectly invoke it?

                      ;._. I really need to learn how to better understand C#.... I also need to learn how to better understand the StardewValley and StardewValley API packages...

                      Edit: Silly question, but I'm noticing that @Jinxiewinxie is using a lot of if statements for time instead of an if-else condition. Is there a reason for that?

                      Edit: How are the shopmenus differentiating the time to when it's closed, leaving a message at counter when it's closed, like Marnie's animal shop, and opening shop when it's open? Is it all in a single method?

                      Is the original Action Gunther recognized as a shop? Where's the sparkling text, before going into the museum menu, coming from for Gunther?
                       
                        Last edited: Jul 25, 2016
                      • Entoarox

                        Entoarox Oxygen Tank

                        That is, while functional, not as code-efficient as using `if, else if, else`, so I would suggest using `else if` myself.

                        As to invoking the original Gunther action, invoke `Game1.currentLocation.performAction("Gunther",Farmer who,Location tileLocation) in your else
                        Where the who and tileLocation have to be setup by you.
                        This lets you invoke a stardew tile-action even if it doesnt actually exist in that tile :p
                         
                        • Gwenhwyfar

                          Gwenhwyfar Subatomic Cosmonaut

                          How would I set up the location? >.>; Sorry for asking so many questions...

                          I understood that for Farmer who, I just put Game1.player. But looking at Location tileLocation, I get confused....

                          checkaction(): already has it set up so that the tile you click on 'xTile.Tiles.Tile tile = Game1.currentLocation.map.GetLayer("Buildings").PickTile(new xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize), Game1.viewport.Size);' should be the location. But if I just put 'tile', it says it's not a valid type...
                           
                          • Entoarox

                            Entoarox Oxygen Tank

                            `new xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize)`.... Your answer is right there :p
                             
                            • Jinxiewinxie

                              Jinxiewinxie Farmer Fashionista

                              I'm just not very good at programming yet ^_^
                               
                              • Gwenhwyfar

                                Gwenhwyfar Subatomic Cosmonaut

                                @Entoarox
                                Yeah, I figured out the problem and I didn't need help for once! :D Woot! But thank you for confirming so I don't have to worry, haha.

                                But instead of just writing 'new xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize', I instead wrote 'public static xTile.Dimensions.Location currentTile;' at the top of the public class and changed the 'new xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize' to 'currentTile = new xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize'.

                                I mostly did that because I wasn't sure what would happen if I just put another 'new xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize' for Location tileLocation, I was afraid the new type would cause it to overwrite the old location. I couldn't set it to just 'xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize' if I removed the new, the code complains there's an error in that. Either way, that acted like a bonus of shortening that line of code.

                                Here's my final product. I noted that if the Action Gunther is invoked anywhere outside the museum, the game will crash. However, it works perfectly fine, as long as the action is placed inside the museum. I'll try throwing in a catch message, just in case someone wants to foolishly move the action elsewhere.

                                I'm going to be doing a test to see if the catch works and then I'll try removing the encoded location to see if I can just add the action through tIDE.
                                Code:
                                using StardewModdingAPI;
                                using StardewModdingAPI.Events;
                                using StardewValley;
                                using Microsoft.Xna.Framework.Input;
                                using System;
                                using Microsoft.Xna.Framework;
                                using xTile.Dimensions;
                                
                                namespace NewGuntherAction
                                {
                                    public class Class1 : Mod
                                    {
                                        public static xTile.Dimensions.Location currentTile;
                                
                                        static void Events_MouseChanged(object sender, EventArgsMouseStateChanged e)
                                        {
                                            if (!Game1.hasLoadedGame) return;
                                            if (e.NewState.RightButton == ButtonState.Pressed && e.PriorState.RightButton != ButtonState.Pressed)
                                            {
                                                CheckForAction();
                                            }
                                        }
                                        public override void Entry(params object[] objects)
                                        {
                                            ControlEvents.MouseChanged += Events_MouseChanged;
                                            ControlEvents.ControllerButtonPressed += Events_ControllerButtonPressed;
                                            GameEvents.UpdateTick += Event_UpdateTick;
                                        }
                                
                                        static void Events_ControllerButtonPressed(object sender, EventArgsControllerButtonPressed e)
                                        {
                                            if (!Game1.hasLoadedGame) return;
                                            if (e.ButtonPressed == Buttons.A)
                                            {
                                                CheckForAction();
                                            }
                                        }
                                
                                        private void Event_UpdateTick(object sender, EventArgs e)
                                        {
                                            if (!Game1.hasLoadedGame) return;
                                            if (!(Game1.currentLocation == Game1.getLocationFromName("LibraryMuseum"))) return;
                                
                                            Game1.getLocationFromName("LibraryMuseum").setTileProperty(3, 8, "Buildings", "Action", "NewGunther");
                                
                                            GameEvents.UpdateTick -= Event_UpdateTick;
                                        }
                                
                                        static void CheckForAction()
                                        {
                                            if (!Game1.player.UsingTool && !Game1.pickingTool && !Game1.menuUp && (!Game1.eventUp || Game1.currentLocation.currentEvent.playerControlSequence) && !Game1.nameSelectUp && Game1.numberOfSelectedItems == -1 && !Game1.fadeToBlack)
                                            {
                                                Vector2 grabTile = new Vector2((float)(Game1.getOldMouseX() + Game1.viewport.X), (float)(Game1.getOldMouseY() + Game1.viewport.Y)) / (float)Game1.tileSize;
                                                if (!Utility.tileWithinRadiusOfPlayer((int)grabTile.X, (int)grabTile.Y, 1, Game1.player))
                                                {
                                                    grabTile = Game1.player.GetGrabTile();
                                                }
                                                xTile.Tiles.Tile tile = Game1.currentLocation.map.GetLayer("Buildings").PickTile(currentTile = new xTile.Dimensions.Location((int)grabTile.X * Game1.tileSize, (int)grabTile.Y * Game1.tileSize), Game1.viewport.Size);
                                                xTile.ObjectModel.PropertyValue propertyValue = null;
                                                if (tile != null)
                                                {
                                                    tile.Properties.TryGetValue("Action", out propertyValue);
                                                }
                                                if (propertyValue != null)
                                                {
                                                    if (propertyValue == "NewGunther") //Change this to your Action property
                                                    {
                                                        if (Game1.timeOfDay < 800 || Game1.timeOfDay > 1800)
                                                        {
                                                            Game1.drawObjectDialogue("The Archeology House Counter is open between 8AM and 6PM. Please come back another time. - Gunther");
                                                        }
                                                        else if (Game1.timeOfDay >= 1230 && Game1.timeOfDay < 1400)
                                                        {
                                                            Game1.drawObjectDialogue("On lunch break! Be back at 2PM. - Gunther");
                                                        }
                                                        else
                                                        {
                                                            if (!(Game1.currentLocation == Game1.getLocationFromName("LibraryMuseum")))
                                                            {
                                                                Game1.drawObjectDialogue("Please place this action inside the Archaeology House only.");
                                                            }
                                                            else
                                                            {
                                                                Game1.currentLocation.performAction("Gunther", Game1.player, currentTile);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                Edit:

                                It's okay, Jinxie! :p I just thought you might have known more than me and there was a certain syntax in the game that prevents you from using else and else if. I've had problems with the xnb file syntax, so it's kinda natural to assume.

                                I'm pretty sure you're at least a bit ahead of me in C# coding though... And SMAPI coding..

                                Edit:

                                With a few fixes, I removed the encoded location and tested it with an edited ArchaeologyHouse tIDE map that has the Action NewGunther. X3 It works beautifully! Also, the catch does a good job of keeping the game from crashing if you try putting the action outside of the Archaeology House.
                                 
                                  Last edited: Jul 25, 2016
                                  Jinxiewinxie likes this.
                                • Jinxiewinxie

                                  Jinxiewinxie Farmer Fashionista

                                  Glad you were able to get it working the way you wanted! =D
                                   
                                  • Gwenhwyfar

                                    Gwenhwyfar Subatomic Cosmonaut

                                    X3 Thanks! I think maybe I should modify the author description in my manifest.json to include you and Entoarox, since I did a good chunk of the coding from you guys. I just whittled it down to my own preference.

                                    Edit: Need to make a minor fix at the 6PM timing since it doesn't close right away at 6PM...
                                     
                                    • Jinxiewinxie

                                      Jinxiewinxie Farmer Fashionista

                                      Coding is basically just regurgitating code that someone else wrote, don't worry about including me =P
                                       
                                      • Entoarox

                                        Entoarox Oxygen Tank

                                        Yeah, when it comes to stardew modding, most of the help we gave you is code that cant be copyrighted anyhow. :p
                                         

                                        Share This Page