Changes

Jump to navigation Jump to search
414 bytes added ,  16:58, 19 February 2021
m
Replace deprecated <source> tags with <syntaxhighlight> tags
Line 16: Line 16:  
All constructors for Object:
 
All constructors for Object:
   −
<source lang='c#'>
+
<syntaxhighlight lang='c#'>
 
  public Object(Vector2 tileLocation, int parentSheetIndex, int initialStack);
 
  public Object(Vector2 tileLocation, int parentSheetIndex, int initialStack);
 
  public Object(Vector2 tileLocation, int parentSheetIndex, bool isRecipe = false);
 
  public Object(Vector2 tileLocation, int parentSheetIndex, bool isRecipe = false);
 
  public Object(int parentSheetIndex, int initialStack, bool isRecipe = false, int price = -1, int quality = 0);
 
  public Object(int parentSheetIndex, int initialStack, bool isRecipe = false, int price = -1, int quality = 0);
 
  public Object(Vector2 tileLocation, int parentSheetIndex, string Givenname, bool canBeSetDown, bool canBeGrabbed, bool isHoedirt, bool isSpawnedObject);
 
  public Object(Vector2 tileLocation, int parentSheetIndex, string Givenname, bool canBeSetDown, bool canBeGrabbed, bool isHoedirt, bool isSpawnedObject);
</source>
+
</syntaxhighlight>
    
Where '''parentSheetIndex''' is the ID of the item (can be found in ObjectInformation.xnb).
 
Where '''parentSheetIndex''' is the ID of the item (can be found in ObjectInformation.xnb).
Line 27: Line 27:  
===Spawn an item on the ground===
 
===Spawn an item on the ground===
   −
<source lang='c#'>
+
<syntaxhighlight lang='c#'>
 
  public virtual bool dropObject(Object obj, Vector2 dropLocation, xTile.Dimensions.Rectangle viewport, bool initialPlacement, Farmer who = null);
 
  public virtual bool dropObject(Object obj, Vector2 dropLocation, xTile.Dimensions.Rectangle viewport, bool initialPlacement, Farmer who = null);
    
  // Concrete code for spawning:
 
  // Concrete code for spawning:
 
  Game1.getLocationFromName("Farm").dropObject(new StardewValley.Object(itemId, 1, false, -1, 0), new Vector2(x, y) * 64f, Game1.viewport, true, (Farmer)null);
 
  Game1.getLocationFromName("Farm").dropObject(new StardewValley.Object(itemId, 1, false, -1, 0), new Vector2(x, y) * 64f, Game1.viewport, true, (Farmer)null);
</source>
+
</syntaxhighlight>
    
===Add an item to an inventory===
 
===Add an item to an inventory===
<source lang='c#'>
+
<syntaxhighlight lang='c#'>
 
//You can add items found in ObjectInformation using:
 
//You can add items found in ObjectInformation using:
 
     Game1.player.addItemByMenuIfNecessary((Item)new StardewValley.Object(int parentSheetIndex, int initialStack, [bool isRecipe = false], [int price = -1], [int quality = 0]));
 
     Game1.player.addItemByMenuIfNecessary((Item)new StardewValley.Object(int parentSheetIndex, int initialStack, [bool isRecipe = false], [int price = -1], [int quality = 0]));
</source>
+
</syntaxhighlight>
    
Another example:
 
Another example:
   −
<source lang='c#'>
+
<syntaxhighlight lang='c#'>
 
     // Add a weapon directly into player's inventory
 
     // Add a weapon directly into player's inventory
 
     const int WEAP_ID = 19;                  // Shadow Dagger -- see Data/weapons
 
     const int WEAP_ID = 19;                  // Shadow Dagger -- see Data/weapons
Line 49: Line 49:     
     // Note: This code WORKS.
 
     // Note: This code WORKS.
</source>
+
</syntaxhighlight>
    
===Remove an item from an inventory===
 
===Remove an item from an inventory===
Line 62: Line 62:  
===Get all locations===
 
===Get all locations===
 
The list of root locations is stored in <tt>Game1.locations</tt>, but constructed building interiors aren't included. This method provides all locations in the game for the main player:
 
The list of root locations is stored in <tt>Game1.locations</tt>, but constructed building interiors aren't included. This method provides all locations in the game for the main player:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
/// <summary>Get all game locations.</summary>
 
/// <summary>Get all game locations.</summary>
 
public static IEnumerable<GameLocation> GetLocations()
 
public static IEnumerable<GameLocation> GetLocations()
Line 74: Line 74:  
         );
 
         );
 
}
 
}
</source>
+
</syntaxhighlight>
    
Then you can use it to iterate all locations:
 
Then you can use it to iterate all locations:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
foreach (GameLocation location in this.GetLocations())
 
foreach (GameLocation location in this.GetLocations())
 
{
 
{
 
   // ...
 
   // ...
 
}
 
}
</source>
+
</syntaxhighlight>
    
Note that farmhands in multiplayer can't see all locations; see [[Modding:Modder Guide/APIs/Multiplayer#GetActiveLocations|<tt>GetActiveLocations</tt>]] instead.
 
Note that farmhands in multiplayer can't see all locations; see [[Modding:Modder Guide/APIs/Multiplayer#GetActiveLocations|<tt>GetActiveLocations</tt>]] instead.
Line 122: Line 122:     
Each tile is 64x64 pixels as specified by <code>Game1.tileSize</code>. The conversion between absolute and tile is as follows:
 
Each tile is 64x64 pixels as specified by <code>Game1.tileSize</code>. The conversion between absolute and tile is as follows:
<source lang='c#'>
+
<syntaxhighlight lang='c#'>
 
// Absolute position => Tile position
 
// Absolute position => Tile position
 
Math.Floor(Game1.player.Position.X / Game1.tileSize)
 
Math.Floor(Game1.player.Position.X / Game1.tileSize)
Line 134: Line 134:  
Math.Floor(Game1.player.currentLocation.Map.DisplayWidth / Game1.tileSize)
 
Math.Floor(Game1.player.currentLocation.Map.DisplayWidth / Game1.tileSize)
 
Math.Floor(Game1.player.currentLocation.Map.DisplayHeight / Game1.tileSize)
 
Math.Floor(Game1.player.currentLocation.Map.DisplayHeight / Game1.tileSize)
</source>
+
</syntaxhighlight>
    
====Position Relative to the Viewport====
 
====Position Relative to the Viewport====
Line 143: Line 143:     
The player's position in pixels relative to the viewport is as follows:
 
The player's position in pixels relative to the viewport is as follows:
<source lang='c#'>
+
<syntaxhighlight lang='c#'>
 
Game1.player.Position.X - Game1.viewport.X
 
Game1.player.Position.X - Game1.viewport.X
 
Game1.player.Position.Y - Game1.viewport.Y
 
Game1.player.Position.Y - Game1.viewport.Y
</source>
+
</syntaxhighlight>
    
==NPC==
 
==NPC==
Line 164: Line 164:  
All of the above can be done with IAssetLoaders/IAssetEditors or Content Patcher. Finally, spawn the NPC with a SMAPI mod. The different constructors are:
 
All of the above can be done with IAssetLoaders/IAssetEditors or Content Patcher. Finally, spawn the NPC with a SMAPI mod. The different constructors are:
   −
<source lang='c#'>
+
<syntaxhighlight lang='c#'>
 
  public NPC(AnimatedSprite sprite, Vector2 position, int facingDir, string name, LocalizedContentManager content = null);
 
  public NPC(AnimatedSprite sprite, Vector2 position, int facingDir, string name, LocalizedContentManager content = null);
 
  public NPC(AnimatedSprite sprite, Vector2 position, string defaultMap, int facingDir, string name, Dictionary<int, int[]> schedule, Texture2D portrait, bool eventActor);
 
  public NPC(AnimatedSprite sprite, Vector2 position, string defaultMap, int facingDir, string name, Dictionary<int, int[]> schedule, Texture2D portrait, bool eventActor);
 
  public NPC(AnimatedSprite sprite, Vector2 position, string defaultMap, int facingDirection, string name, bool datable, Dictionary<int, int[]> schedule, Texture2D portrait);
 
  public NPC(AnimatedSprite sprite, Vector2 position, string defaultMap, int facingDirection, string name, bool datable, Dictionary<int, int[]> schedule, Texture2D portrait);
</source>
+
</syntaxhighlight>
    
For spawning:
 
For spawning:
   −
<source lang='c#'>
+
<syntaxhighlight lang='c#'>
 
  Game1.getLocationFromName("Town").addCharacter(npc);
 
  Game1.getLocationFromName("Town").addCharacter(npc);
</source>
+
</syntaxhighlight>
    
==User-interface (UI)==
 
==User-interface (UI)==
Line 184: Line 184:  
HUDMessage are those popups in the lower left hand screen. They have several constructors, which we will briefly go over here (a few non-relevant constructors are not included):
 
HUDMessage are those popups in the lower left hand screen. They have several constructors, which we will briefly go over here (a few non-relevant constructors are not included):
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
   public HUDMessage(string message);
 
   public HUDMessage(string message);
 
   public HUDMessage(string message, int whatType);
 
   public HUDMessage(string message, int whatType);
Line 190: Line 190:  
   public HUDMessage(string message, string leaveMeNull)
 
   public HUDMessage(string message, string leaveMeNull)
 
   public HUDMessage(string message, Color color, float timeLeft, bool fadeIn)
 
   public HUDMessage(string message, Color color, float timeLeft, bool fadeIn)
</source>
+
</syntaxhighlight>
      Line 221: Line 221:     
For example: add a new HUDMessage to show [[File:Error-image-ingame.png]] toaster popup.  
 
For example: add a new HUDMessage to show [[File:Error-image-ingame.png]] toaster popup.  
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
Game1.addHUDMessage(new HUDMessage("MESSAGE", 3));
 
Game1.addHUDMessage(new HUDMessage("MESSAGE", 3));
</source>
+
</syntaxhighlight>
    
===Active clickable menu===
 
===Active clickable menu===
Line 229: Line 229:     
Each menu is different, so you need to look at the menu code to know how to interact with it. Since mods often need to get the current tab on the game menu, here's an example which handles the map tab:
 
Each menu is different, so you need to look at the menu code to know how to interact with it. Since mods often need to get the current tab on the game menu, here's an example which handles the map tab:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
if (Game1.activeClickableMenu is GameMenu menu)
 
if (Game1.activeClickableMenu is GameMenu menu)
 
{
 
{
Line 249: Line 249:  
   }
 
   }
 
}
 
}
</source>
+
</syntaxhighlight>
    
To create a custom menu, you need to create a subclass of <tt>IClickableMenu</tt> and assign it to <tt>Game1.activeClickableMenu</tt>. At its most basic, a menu is basically just a few methods you override (usually <tt>draw</tt> and <tt>receiveLeftClick</tt> at a minimum). When <tt>draw</tt> is called, you draw whatever you want to the screen; when <tt>receiveLeftClick</tt> is called, you check if it's within one of the clickable areas and handle it. Normally you'd use some convenience classes like <tt>ClickableTextureButton</tt> (which has a texture and position, and simplifies checking if they were clicked), though that's not strictly necessary. Here's [https://github.com/janavarro95/Stardew_Valley_Mods/blob/master/GeneralMods/HappyBirthday/Framework/BirthdayMenu.cs a simple menu] you can use as an example, which draws the birthday menu for {{nexus mod|520|Birthday Mod}}.
 
To create a custom menu, you need to create a subclass of <tt>IClickableMenu</tt> and assign it to <tt>Game1.activeClickableMenu</tt>. At its most basic, a menu is basically just a few methods you override (usually <tt>draw</tt> and <tt>receiveLeftClick</tt> at a minimum). When <tt>draw</tt> is called, you draw whatever you want to the screen; when <tt>receiveLeftClick</tt> is called, you check if it's within one of the clickable areas and handle it. Normally you'd use some convenience classes like <tt>ClickableTextureButton</tt> (which has a texture and position, and simplifies checking if they were clicked), though that's not strictly necessary. Here's [https://github.com/janavarro95/Stardew_Valley_Mods/blob/master/GeneralMods/HappyBirthday/Framework/BirthdayMenu.cs a simple menu] you can use as an example, which draws the birthday menu for {{nexus mod|520|Birthday Mod}}.
Line 262: Line 262:  
Here is an example of a '''simple, choiceless output:'''
 
Here is an example of a '''simple, choiceless output:'''
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
using StardewValley.Menus;  // This is where the DialogueBox class lives
 
using StardewValley.Menus;  // This is where the DialogueBox class lives
    
string message = "This looks like a typewriter ... ^But it's not ...^It's a computer.^";
 
string message = "This looks like a typewriter ... ^But it's not ...^It's a computer.^";
 
Game1.activeClickableMenu = new DialogueBox(message);
 
Game1.activeClickableMenu = new DialogueBox(message);
</source>
+
</syntaxhighlight>
    
// TODO: Examples with choices
 
// TODO: Examples with choices
Line 289: Line 289:  
The example below adds 4 letters into the mail data collection.  Note, that the code below does not send any letters to the player, but simply makes them available to Stardew Valley game so they can be sent.
 
The example below adds 4 letters into the mail data collection.  Note, that the code below does not send any letters to the player, but simply makes them available to Stardew Valley game so they can be sent.
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
using StardewModdingAPI;
 
using StardewModdingAPI;
   Line 321: Line 321:  
     }
 
     }
 
}
 
}
</source>
+
</syntaxhighlight>
    
===Send a letter (using static content)===
 
===Send a letter (using static content)===
Line 327: Line 327:  
To make uses of this class in your own project, thereby making the static mail data available, hook into the OnGameLaunch event, for example.
 
To make uses of this class in your own project, thereby making the static mail data available, hook into the OnGameLaunch event, for example.
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
     /// <summary>
 
     /// <summary>
 
     /// Fires after game is launched, right before first update tick. Happens once per game session (unrelated to loading saves).
 
     /// Fires after game is launched, right before first update tick. Happens once per game session (unrelated to loading saves).
Line 336: Line 336:  
         Helper.Content.AssetEditors.Add(new MyModMail());
 
         Helper.Content.AssetEditors.Add(new MyModMail());
 
     }
 
     }
</source>
+
</syntaxhighlight>
    
Now that you have your letter loaded, it's time to send it to the player.  There are a couple different methods available to accomplish this as well, depending on your need.  Two examples are shown below.  The distinction between the two methods will be explained below:
 
Now that you have your letter loaded, it's time to send it to the player.  There are a couple different methods available to accomplish this as well, depending on your need.  Two examples are shown below.  The distinction between the two methods will be explained below:
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
     Game1.player.mailbox.Add("MyModMail1");
 
     Game1.player.mailbox.Add("MyModMail1");
 
     Game1.addMailForTomorrow("MyModMail2");
 
     Game1.addMailForTomorrow("MyModMail2");
</source>
+
</syntaxhighlight>
    
The first method (Game1.player.mailbox.Add) adds the letter directly into the mailbox for the current day.  This can be accomplished in your "DayStaring" event code, for example.  Mail added directly to the mailbox is not "remembered" as being sent even after a save.  This is useful in some scenarios depending on your need.
 
The first method (Game1.player.mailbox.Add) adds the letter directly into the mailbox for the current day.  This can be accomplished in your "DayStaring" event code, for example.  Mail added directly to the mailbox is not "remembered" as being sent even after a save.  This is useful in some scenarios depending on your need.
Line 353: Line 353:  
If you want Stardew Valley to forget that a specific letter has already been sent, you can remove it from the mailReceived collection.  You can iterate through the collection as well using a foreach should you need to remove mail en mass.
 
If you want Stardew Valley to forget that a specific letter has already been sent, you can remove it from the mailReceived collection.  You can iterate through the collection as well using a foreach should you need to remove mail en mass.
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
     Game1.player.mailReceived.Remove("MyModMail1");
 
     Game1.player.mailReceived.Remove("MyModMail1");
</source>
+
</syntaxhighlight>
    
That is all there is to sending a simple letter.  Attaching objects and sending money via letter is straight-forward, but sending recipes is more complicated  and will need some additional explanation at a future time.
 
That is all there is to sending a simple letter.  Attaching objects and sending money via letter is straight-forward, but sending recipes is more complicated  and will need some additional explanation at a future time.
Line 365: Line 365:  
Consider the following source code, which is basically an enhanced version of the static mail class shown above, that will also support "dynamic" content.  You could certainly always use the enhanced version of this code and just not make use of the dynamic content unless needed.  The code was separated for the purpose of illustrating the differences.
 
Consider the following source code, which is basically an enhanced version of the static mail class shown above, that will also support "dynamic" content.  You could certainly always use the enhanced version of this code and just not make use of the dynamic content unless needed.  The code was separated for the purpose of illustrating the differences.
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
using StardewModdingAPI;
 
using StardewModdingAPI;
 
using System.Collections.Generic;
 
using System.Collections.Generic;
Line 420: Line 420:  
}
 
}
   −
</source>
+
</syntaxhighlight>
    
You will notice that there is really very little difference in the code used for static mail and dynamic mail.  The class that supports dynamic mail has a private dictionary collection for holding on to any mail content waiting to be injected.  It could have been made public to allow mail to be added directly into the collection, but that is not good practice.  Instead a public Add method was provided so that mail could be sent, so to speak, to the collection.  This code is for a specific MOD, not a robust framework, so it isn't overly concerned with error handling. You can improve that based on your needs.
 
You will notice that there is really very little difference in the code used for static mail and dynamic mail.  The class that supports dynamic mail has a private dictionary collection for holding on to any mail content waiting to be injected.  It could have been made public to allow mail to be added directly into the collection, but that is not good practice.  Instead a public Add method was provided so that mail could be sent, so to speak, to the collection.  This code is for a specific MOD, not a robust framework, so it isn't overly concerned with error handling. You can improve that based on your needs.
Line 430: Line 430:  
To make uses of this class in your own project, thereby making the dynamic mail available, hook into the OnGameLaunch event, for example.
 
To make uses of this class in your own project, thereby making the dynamic mail available, hook into the OnGameLaunch event, for example.
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
     // Make this available to other methods in the class to access
 
     // Make this available to other methods in the class to access
 
     private MailData mailData = new MailData();
 
     private MailData mailData = new MailData();
Line 442: Line 442:  
         Helper.Content.AssetEditors.Add(mailData);
 
         Helper.Content.AssetEditors.Add(mailData);
 
     }
 
     }
</source>
+
</syntaxhighlight>
    
You can hook into other events, such as "Day Starting" or "Day Ending" to generate the letter you want to send.  Consider this simple example, that is only for illustration purposes.
 
You can hook into other events, such as "Day Starting" or "Day Ending" to generate the letter you want to send.  Consider this simple example, that is only for illustration purposes.
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
     private void OnDayStarting(object sender, DayStartedEventArgs e)
 
     private void OnDayStarting(object sender, DayStartedEventArgs e)
 
     {
 
     {
Line 457: Line 457:  
         modHelper.Content.InvalidateCache("Data\\mail"); // (modHelper was assigned in ModEntry for use throughout the class)
 
         modHelper.Content.InvalidateCache("Data\\mail"); // (modHelper was assigned in ModEntry for use throughout the class)
 
     }
 
     }
</source>
+
</syntaxhighlight>
    
This example formats a letter, showing the up-to-date count of rabbit wool, makes it available to the mail collection, places that letter in the mailbox, and then invalidates the cache so that this new letter will be injected during the cache refresh.  In this case there is no need to remember mailId as the letter will be recreated each time it needs to be sent, which in this example is everyday.  Again, this code is only for illustration of the concept.
 
This example formats a letter, showing the up-to-date count of rabbit wool, makes it available to the mail collection, places that letter in the mailbox, and then invalidates the cache so that this new letter will be injected during the cache refresh.  In this case there is no need to remember mailId as the letter will be recreated each time it needs to be sent, which in this example is everyday.  Again, this code is only for illustration of the concept.
Line 467: Line 467:  
==Other==
 
==Other==
 
===Add a small animation===
 
===Add a small animation===
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
location.temporarySprites.Add(new TemporaryAnimatedSprite(...))
 
location.temporarySprites.Add(new TemporaryAnimatedSprite(...))
</source>
+
</syntaxhighlight>
 
See ''TemporaryAnimatedSprite'' for more details
 
See ''TemporaryAnimatedSprite'' for more details
    
===Play a sound===
 
===Play a sound===
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
location.playSound("SOUND");  
 
location.playSound("SOUND");  
</source>
+
</syntaxhighlight>
 
(e.g. "junimoMeep1")
 
(e.g. "junimoMeep1")
  
114

edits

Navigation menu