Changes

m
Text replacement - "tt>" to "samp>"
Line 5: Line 5:  
==Intro==
 
==Intro==
 
===What's an 'asset'?===
 
===What's an 'asset'?===
An ''asset'' is one image, map, or data structure provided to the game. The game stores its default assets in its <tt>Content</tt> folder, though mods can have custom assets too. For example, all of Abigail's portraits are stored in one asset inside <tt>Content\Portraits\Abigail.xnb</tt>. If you [[Modding:Editing XNB files|unpack that file]], you'll see it contains an image file:
+
An ''asset'' is one image, map, or data structure provided to the game. The game stores its default assets in its <samp>Content</samp> folder, though mods can have custom assets too. For example, all of Abigail's portraits are stored in one asset inside <samp>Content\Portraits\Abigail.xnb</samp>. If you [[Modding:Editing XNB files|unpack that file]], you'll see it contains an image file:
    
[[File:Modding - creating an XNB mod - example portraits.png]]
 
[[File:Modding - creating an XNB mod - example portraits.png]]
Line 12: Line 12:     
===What's an 'asset name'?===
 
===What's an 'asset name'?===
An ''asset name'' identifies an asset. For a <tt>Content</tt> file, this is the file path relative to <tt>Content</tt> ''without'' the <tt>.xnb</tt> extension or language. For example, <tt>Content\Maps\Desert.xnb</tt> and <tt>Content\Maps\Desert.ja-JA.xnb</tt> both have asset name <tt>Maps\Desert</tt>.
+
An ''asset name'' identifies an asset. For a <samp>Content</samp> file, this is the file path relative to <samp>Content</samp> ''without'' the <samp>.xnb</samp> extension or language. For example, <samp>Content\Maps\Desert.xnb</samp> and <samp>Content\Maps\Desert.ja-JA.xnb</samp> both have asset name <samp>Maps\Desert</samp>.
    
===What does the content API do?===
 
===What does the content API do?===
 
SMAPI handles content loading for the game. This lets you...
 
SMAPI handles content loading for the game. This lets you...
* read data, images, or maps from your mod folder (with support for <tt>.json</tt>, <tt>.png</tt>, <tt>.tbin</tt>, <tt>.tmx</tt>, and <tt>.xnb</tt> files);
+
* read data, images, or maps from your mod folder (with support for <samp>.json</samp>, <samp>.png</samp>, <samp>.tbin</samp>, <samp>.tmx</samp>, and <samp>.xnb</samp> files);
* read assets from the game's <tt>Content</tt> folder;
+
* read assets from the game's <samp>Content</samp> folder;
 
* make changes to game assets (without changing the actual files);
 
* make changes to game assets (without changing the actual files);
 
* provide new assets to the game.
 
* provide new assets to the game.
Line 45: Line 45:  
! notes
 
! notes
 
|-
 
|-
| <tt>.xnb</tt>
+
| <samp>.xnb</samp>
 
| ''any''
 
| ''any''
| A packed file, like those in the game's <tt>Content</tt> folder. Not recommended since it's harder to edit and maintain.
+
| A packed file, like those in the game's <samp>Content</samp> folder. Not recommended since it's harder to edit and maintain.
 
|-
 
|-
| <tt>.json</tt>
+
| <samp>.json</samp>
| <tt>''any''</tt>
+
| <samp>''any''</samp>
| A data file, typically used to store <tt>Dictionary&lt;int, string&gt;</tt> or <tt>Dictionary&lt;string, string&gt;</tt> data.
+
| A data file, typically used to store <samp>Dictionary&lt;int, string&gt;</samp> or <samp>Dictionary&lt;string, string&gt;</samp> data.
 
|-
 
|-
| <tt>.png</tt>
+
| <samp>.png</samp>
| <tt>[https://docs.microsoft.com/en-us/previous-versions/windows/xna/bb199316%28v%3dxnagamestudio.41%29 Texture2D]</tt>
+
| <samp>[https://docs.microsoft.com/en-us/previous-versions/windows/xna/bb199316%28v%3dxnagamestudio.41%29 Texture2D]</samp>
 
| An image file. You can use this to load textures, spritesheets, tilesheets, etc.
 
| An image file. You can use this to load textures, spritesheets, tilesheets, etc.
 
|-
 
|-
| <tt>.tbin</tt> or <tt>.tmx</tt>
+
| <samp>.tbin</samp> or <samp>.tmx</samp>
| <tt>xTile.Map</tt>
+
| <samp>xTile.Map</samp>
| A map file, which can be used to create or modify an in-game location. SMAPI will automatically match tilesheets to image files in the same folder as the map if they exist; otherwise the game will check the <tt>Content</tt> folders for them.
+
| A map file, which can be used to create or modify an in-game location. SMAPI will automatically match tilesheets to image files in the same folder as the map if they exist; otherwise the game will check the <samp>Content</samp> folders for them.
 
|}
 
|}
    
Some usage notes:
 
Some usage notes:
* The normal convention is to have them in an <tt>assets</tt> subfolder, though that's not required.
+
* The normal convention is to have them in an <samp>assets</samp> subfolder, though that's not required.
 
* Don't worry about which path separators you use; SMAPI will normalise them automatically.
 
* Don't worry about which path separators you use; SMAPI will normalise them automatically.
* To avoid performance issues, don't call <tt>content.Load<T></tt> repeatedly in draw code. Instead, load your asset once and reuse it.
+
* To avoid performance issues, don't call <samp>content.Load<T></samp> repeatedly in draw code. Instead, load your asset once and reuse it.
    
===Get actual mod asset keys===
 
===Get actual mod asset keys===
Line 79: Line 79:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
Note that this requires the asset name, ''not'' a filename. You can get the asset name by taking the path relative to the <tt>Content</tt> folder, and removing the language code and <tt>.xnb</tt> extension. See [[#What's an 'asset'?]].
+
Note that this requires the asset name, ''not'' a filename. You can get the asset name by taking the path relative to the <samp>Content</samp> folder, and removing the language code and <samp>.xnb</samp> extension. See [[#What's an 'asset'?]].
    
==Replace a game asset==
 
==Replace a game asset==
Line 85: Line 85:  
You can replace an asset entirely by providing the asset to SMAPI yourself. The original file won't be read at all and won't be changed.
 
You can replace an asset entirely by providing the asset to SMAPI yourself. The original file won't be read at all and won't be changed.
   −
You can do this by implementing <tt>IAssetLoader</tt> in your <tt>Mod</tt> class. This adds two methods: <tt>CanLoad&lt;T&gt;</tt> returns whether the mod can provide a particular asset, and <tt>Load&lt;T&gt;</tt> provides the asset data. SMAPI will call your <tt>CanLoad&lt;T&gt;</tt> every time an asset is loaded (which may happen multiple times per asset), then call <tt>Load&lt;T&gt;</tt> if it returns true. (If multiple mods return true from <tt>CanLoad&lt;T&gt;</tt> for the same asset, SMAPI will display an error and not call <tt>Load&lt;T&gt;</tt> for any of them.)
+
You can do this by implementing <samp>IAssetLoader</samp> in your <samp>Mod</samp> class. This adds two methods: <samp>CanLoad&lt;T&gt;</samp> returns whether the mod can provide a particular asset, and <samp>Load&lt;T&gt;</samp> provides the asset data. SMAPI will call your <samp>CanLoad&lt;T&gt;</samp> every time an asset is loaded (which may happen multiple times per asset), then call <samp>Load&lt;T&gt;</samp> if it returns true. (If multiple mods return true from <samp>CanLoad&lt;T&gt;</samp> for the same asset, SMAPI will display an error and not call <samp>Load&lt;T&gt;</samp> for any of them.)
    
For example, here's a mod which replaces Abigail's portraits with a custom version from its mod folder:
 
For example, here's a mod which replaces Abigail's portraits with a custom version from its mod folder:
Line 119: Line 119:     
===Replace a map file===
 
===Replace a map file===
You can use <tt>IAssetLoader</tt> to load custom maps too. When you load a map file, and an unpacked tilesheet is present in the mod folder (relative to the map file), SMAPI will automatically link the map to that file and handle loading it too. If the tilesheet filename starts with a season and underscore, the game will apply its normal seasonal logic to it too.
+
You can use <samp>IAssetLoader</samp> to load custom maps too. When you load a map file, and an unpacked tilesheet is present in the mod folder (relative to the map file), SMAPI will automatically link the map to that file and handle loading it too. If the tilesheet filename starts with a season and underscore, the game will apply its normal seasonal logic to it too.
    
For example, let's say you have a mod with this structure:
 
For example, let's say you have a mod with this structure:
Line 154: Line 154:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
That's it! SMAPI will detect a reference to <tt>spring_customTilesheet.png</tt>, find the file relative to the map file, and load it too. When the season changes in-game, SMAPI will automatically switch it to <tt>summer_customTilesheet.png</tt>, etc. The other tilesheet references will be left untouched (since there's no local file), and use the Content files.
+
That's it! SMAPI will detect a reference to <samp>spring_customTilesheet.png</samp>, find the file relative to the map file, and load it too. When the season changes in-game, SMAPI will automatically switch it to <samp>summer_customTilesheet.png</samp>, etc. The other tilesheet references will be left untouched (since there's no local file), and use the Content files.
    
===Add a new asset===
 
===Add a new asset===
Line 185: Line 185:  
==Edit a game asset==
 
==Edit a game asset==
 
===Basics===
 
===Basics===
You can edit any game asset after it's loaded (but before it's provided to the game), without changing the original files. You do this by implementing <tt>IAssetEditor</tt> in your <tt>Mod</tt> class, which adds two methods: <tt>CanEdit&lt;T&gt;</tt> returns whether the mod can edit a particular asset, and <tt>Edit&lt;T&gt;</tt> makes any changes needed. SMAPI will call your <tt>CanEdit&lt;T&gt;</tt> every time an asset is loaded (which may happen multiple times per asset), then call <tt>Edit&lt;T&gt;</tt> if it returns true.
+
You can edit any game asset after it's loaded (but before it's provided to the game), without changing the original files. You do this by implementing <samp>IAssetEditor</samp> in your <samp>Mod</samp> class, which adds two methods: <samp>CanEdit&lt;T&gt;</samp> returns whether the mod can edit a particular asset, and <samp>Edit&lt;T&gt;</samp> makes any changes needed. SMAPI will call your <samp>CanEdit&lt;T&gt;</samp> every time an asset is loaded (which may happen multiple times per asset), then call <samp>Edit&lt;T&gt;</samp> if it returns true.
    
For example, here's a mod which doubles the sale price of all items:
 
For example, here's a mod which doubles the sale price of all items:
Line 220: Line 220:  
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Note that you will run into errors if you attempt to modify the collection you're iterating over. To get around this, we use the <tt>ToArray</tt>() function as above, to iterate over a copy of the collection instead.
+
Note that you will run into errors if you attempt to modify the collection you're iterating over. To get around this, we use the <samp>ToArray</samp>() function as above, to iterate over a copy of the collection instead.
   −
The <tt>IAssetData asset</tt> argument for your <tt>Edit</tt> method has some helpers to make editing data easier, documented below. (See IntelliSense for the <tt>asset</tt> argument for more info.)
+
The <samp>IAssetData asset</samp> argument for your <samp>Edit</samp> method has some helpers to make editing data easier, documented below. (See IntelliSense for the <samp>asset</samp> argument for more info.)
    
===Edit any file===
 
===Edit any file===
These fields/methods are available directly on the <tt>asset</tt> received by your <tt>Edit</tt> method for any asset type, and also available through the helpers listed below.
+
These fields/methods are available directly on the <samp>asset</samp> received by your <samp>Edit</samp> method for any asset type, and also available through the helpers listed below.
    
; Data
 
; Data
Line 243: Line 243:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
You can get a dictionary helper using <tt>asset.AsDictionary<TKey, string>()</tt>, where <tt>TKey</tt> is replaced with the key type (usually <tt>int</tt> or <tt>string</tt>).
+
You can get a dictionary helper using <samp>asset.AsDictionary<TKey, string>()</samp>, where <samp>TKey</samp> is replaced with the key type (usually <samp>int</samp> or <samp>string</samp>).
    
; Data
 
; Data
Line 256: Line 256:     
===Edit an image===
 
===Edit an image===
When editing an image file, you can get a helper using <tt>asset.AsImage()</tt>.
+
When editing an image file, you can get a helper using <samp>asset.AsImage()</samp>.
    
; Data
 
; Data
Line 279: Line 279:  
! usage
 
! usage
 
|-
 
|-
| <tt>source</tt>
+
| <samp>source</samp>
 
| The source image to copy & paste onto the loaded image.
 
| The source image to copy & paste onto the loaded image.
 
|-
 
|-
| <tt>sourceArea</tt>
+
| <samp>sourceArea</samp>
 
| ''(optional)'' The pixel area within the source image to copy (or omit to use the entire source image). This must fit within the target image.
 
| ''(optional)'' The pixel area within the source image to copy (or omit to use the entire source image). This must fit within the target image.
 
|-
 
|-
| <tt>targetArea</tt>
+
| <samp>targetArea</samp>
 
| ''(optional)'' The pixel area within the loaded image to replace (or omit to replace starting from the top-left corner up to the full source size).
 
| ''(optional)'' The pixel area within the loaded image to replace (or omit to replace starting from the top-left corner up to the full source size).
 
|-
 
|-
| <tt>patchMode</tt>
+
| <samp>patchMode</samp>
 
| ''(optional)'' How the image should be patched. The possible values...
 
| ''(optional)'' How the image should be patched. The possible values...
* <tt>PatchMode.Replace</tt> (default): erase the original content within the area before pasting in the new content;
+
* <samp>PatchMode.Replace</samp> (default): erase the original content within the area before pasting in the new content;
* <tt>PatchMode.Overlay</tt>: draw the new content over the original content, so the original content shows through any ''fully'' transparent pixels.
+
* <samp>PatchMode.Overlay</samp>: draw the new content over the original content, so the original content shows through any ''fully'' transparent pixels.
 
|}
 
|}
   Line 311: Line 311:  
! usage
 
! usage
 
|-
 
|-
| <tt>minWidth</tt>
+
| <samp>minWidth</samp>
 
| The minimum desired width. If the image width is less than this value, it'll be extended on the right up to that size.
 
| The minimum desired width. If the image width is less than this value, it'll be extended on the right up to that size.
 
|-
 
|-
| <tt>minHeight</tt>
+
| <samp>minHeight</samp>
 
| The minimum desired height. If the image height is less than this value, it'll be extended from the bottom up to that size.
 
| The minimum desired height. If the image height is less than this value, it'll be extended from the bottom up to that size.
 
|}
 
|}
    
===Edit a map===
 
===Edit a map===
When editing a map file, you can get a helper using <tt>asset.AsMap()</tt>.
+
When editing a map file, you can get a helper using <samp>asset.AsMap()</samp>.
    
; Data
 
; Data
Line 342: Line 342:  
! usage
 
! usage
 
|-
 
|-
| <tt>source</tt>
+
| <samp>source</samp>
 
| The source map to copy & paste onto the loaded map.
 
| The source map to copy & paste onto the loaded map.
 
|-
 
|-
| <tt>sourceArea</tt>
+
| <samp>sourceArea</samp>
 
| ''(optional)'' The tile area within the source map to copy (or omit to use the entire source map). This must fit within the target map.
 
| ''(optional)'' The tile area within the source map to copy (or omit to use the entire source map). This must fit within the target map.
 
|-
 
|-
| <tt>targetArea</tt>
+
| <samp>targetArea</samp>
 
| ''(optional)'' The tile area within the loaded map to replace (or omit to replace starting from the top-left corner up to the full source size).
 
| ''(optional)'' The tile area within the loaded map to replace (or omit to replace starting from the top-left corner up to the full source size).
 
|-
 
|-
| <tt>patchMode</tt>
+
| <samp>patchMode</samp>
 
| {{SMAPI upcoming|3.13.0|content=''(optional)'' How to merge tiles into the target map. The default is <code>ReplaceByLayer</code>.
 
| {{SMAPI upcoming|3.13.0|content=''(optional)'' How to merge tiles into the target map. The default is <code>ReplaceByLayer</code>.
   −
For example, assume a mostly empty source map with two layers: <tt>Back</tt> (red) and <tt>Buildings</tt> (blue):<br />[[File:SMAPI content API - map patch mode - source.png]]
+
For example, assume a mostly empty source map with two layers: <samp>Back</samp> (red) and <samp>Buildings</samp> (blue):<br />[[File:SMAPI content API - map patch mode - source.png]]
    
Here's how that would be merged with each patch mode (black areas are the empty void under the map):
 
Here's how that would be merged with each patch mode (black areas are the empty void under the map):
 
<gallery>
 
<gallery>
File:SMAPI content API - map patch mode - overlay.png|<code>Overlay</code>: only matching tiles are replaced. The red tile replaces the ground on the <tt>Back</tt> layer, but the ground is visible under the blue <tt>Buildings</tt> tile.
+
File:SMAPI content API - map patch mode - overlay.png|<code>Overlay</code>: only matching tiles are replaced. The red tile replaces the ground on the <samp>Back</samp> layer, but the ground is visible under the blue <samp>Buildings</samp> tile.
 
File:SMAPI content API - map patch mode - replace by layer.png|<code>ReplaceByLayer</code> ''(default)'': all tiles are replaced, but only on layers that exist in the source map.
 
File:SMAPI content API - map patch mode - replace by layer.png|<code>ReplaceByLayer</code> ''(default)'': all tiles are replaced, but only on layers that exist in the source map.
 
File:SMAPI content API - map patch mode - replace.png|<code>Replace</code>: all tiles are replaced.
 
File:SMAPI content API - map patch mode - replace.png|<code>Replace</code>: all tiles are replaced.
Line 366: Line 366:  
==Advanced==
 
==Advanced==
 
===Compare asset names===
 
===Compare asset names===
You can't compare asset names directly because they depend on the current OS (e.g. <tt>Characters/Abigail</tt> versus <tt>Characters\Abigail</tt>) and they're case-insensitive (e.g. <tt>Characters/ABIGAIL</tt>).
+
You can't compare asset names directly because they depend on the current OS (e.g. <samp>Characters/Abigail</samp> versus <samp>Characters\Abigail</samp>) and they're case-insensitive (e.g. <samp>Characters/ABIGAIL</samp>).
   −
To check for an exact match when you have an <tt>IAssetData</tt> instance (e.g. from the content API), just call <code>asset.AssetNameEquals("Characters/Abigail")</code> and SMAPI will normalize the asset names internally.
+
To check for an exact match when you have an <samp>IAssetData</samp> instance (e.g. from the content API), just call <code>asset.AssetNameEquals("Characters/Abigail")</code> and SMAPI will normalize the asset names internally.
   −
If you need to check manually, you should normalize the asset names using [[Modding:Modder Guide/APIs/Utilities#File paths|<tt>PathUtilities</tt>]] and compare case-insensitively. For example:
+
If you need to check manually, you should normalize the asset names using [[Modding:Modder Guide/APIs/Utilities#File paths|<samp>PathUtilities</samp>]] and compare case-insensitively. For example:
 
<syntaxhighlight lang="c#">
 
<syntaxhighlight lang="c#">
 
string dialoguePrefix = PathUtilities.NormalizePath(@"Characters\Dialogue\");
 
string dialoguePrefix = PathUtilities.NormalizePath(@"Characters\Dialogue\");
Line 392: Line 392:     
===Create separate asset editors/loaders===
 
===Create separate asset editors/loaders===
All the examples above say to implement <tt>IAssetEditor</tt> or <tt>IAssetLoader</tt> directly on your mod class. That's fine in the vast majority of cases, but you can also provide separate instances instead:
+
All the examples above say to implement <samp>IAssetEditor</samp> or <samp>IAssetLoader</samp> directly on your mod class. That's fine in the vast majority of cases, but you can also provide separate instances instead:
 
<syntaxhighlight lang="c#">
 
<syntaxhighlight lang="c#">
 
public class ModEntry : Mod
 
public class ModEntry : Mod
Line 406: Line 406:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
When you add or remove an asset editor/loader, SMAPI will call their <tt>CanEdit</tt> and <tt>CanLoad</tt> methods for all loaded assets and reload matched assets. This is an expensive process when done outside your <tt>Entry</tt> method, so avoid adding editors/loaders unnecessarily.
+
When you add or remove an asset editor/loader, SMAPI will call their <samp>CanEdit</samp> and <samp>CanLoad</samp> methods for all loaded assets and reload matched assets. This is an expensive process when done outside your <samp>Entry</samp> method, so avoid adding editors/loaders unnecessarily.
    
===Patch helper for custom assets===
 
===Patch helper for custom assets===
106,035

edits