Changes

Jump to navigation Jump to search
m
Replace deprecated <source> tags with <syntaxhighlight> tags
Line 26: Line 26:  
===Read mod assets===
 
===Read mod assets===
 
You can read custom assets from your mod folder by specifying its path (relative to your mod folder) and type. For example:
 
You can read custom assets from your mod folder by specifying its path (relative to your mod folder) and type. For example:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
// read an image file
 
// read an image file
 
Texture2D texture = helper.Content.Load<Texture2D>("assets/texture.png", ContentSource.ModFolder);
 
Texture2D texture = helper.Content.Load<Texture2D>("assets/texture.png", ContentSource.ModFolder);
Line 35: Line 35:  
// read a data file
 
// read a data file
 
IDictionary<string, string> data = helper.Content.Load<Dictionary<string, string>>("assets/data.json", ContentSource.ModFolder);
 
IDictionary<string, string> data = helper.Content.Load<Dictionary<string, string>>("assets/data.json", ContentSource.ModFolder);
</source>
+
</syntaxhighlight>
    
The supported file types are...
 
The supported file types are...
Line 69: Line 69:  
===Get actual mod asset keys===
 
===Get actual mod asset keys===
 
When you load an asset from your mod folder, SMAPI stores it with an asset name that uniquely identifies it. If you need to pass the asset name to game code, you can retrieve the actual asset name:
 
When you load an asset from your mod folder, SMAPI stores it with an asset name that uniquely identifies it. If you need to pass the asset name to game code, you can retrieve the actual asset name:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
tilesheet.ImageSource = helper.Content.GetActualAssetKey("assets/tilesheet.png", ContentSource.ModFolder);
 
tilesheet.ImageSource = helper.Content.GetActualAssetKey("assets/tilesheet.png", ContentSource.ModFolder);
</source>
+
</syntaxhighlight>
    
===Read content assets===
 
===Read content assets===
 
You can also read assets from the game folder:
 
You can also read assets from the game folder:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
Texture2D portraits = helper.Content.Load<Texture2D>("Portraits/Abigail", ContentSource.GameContent);
 
Texture2D portraits = helper.Content.Load<Texture2D>("Portraits/Abigail", ContentSource.GameContent);
</source>
+
</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 <tt>Content</tt> folder, and removing the language code and <tt>.xnb</tt> extension. See [[#What's an 'asset'?]].
Line 89: Line 89:  
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:
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
public class ModEntry : Mod, IAssetLoader
 
public class ModEntry : Mod, IAssetLoader
 
{
 
{
Line 116: Line 116:  
     }
 
     }
 
}
 
}
</source>
+
</syntaxhighlight>
    
===Replace a map file===
 
===Replace a map file===
Line 135: Line 135:  
You can load the custom map like this:
 
You can load the custom map like this:
   −
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
public class ModEntry : Mod, IAssetLoader
 
public class ModEntry : Mod, IAssetLoader
 
{
 
{
Line 152: Line 152:  
     }
 
     }
 
}
 
}
</source>
+
</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 <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.
Line 158: Line 158:  
===Add a new asset===
 
===Add a new asset===
 
Providing a new asset is exactly like replacing an existing one (see previous section). For example, this code adds a new dialogue file for a custom NPC:
 
Providing a new asset is exactly like replacing an existing one (see previous section). For example, this code adds a new dialogue file for a custom NPC:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
public class ModEntry : Mod, IAssetLoader
 
public class ModEntry : Mod, IAssetLoader
 
{
 
{
Line 178: Line 178:  
     }
 
     }
 
}
 
}
</source>
+
</syntaxhighlight>
    
===Disadvantages===
 
===Disadvantages===
Line 188: Line 188:     
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:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
public class ModEntry : Mod, IAssetEditor
 
public class ModEntry : Mod, IAssetEditor
 
{
 
{
Line 219: Line 219:  
     }
 
     }
 
}
 
}
</source>
+
</syntaxhighlight>
    
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 <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.)
Line 234: Line 234:  
===Edit a dictionary===
 
===Edit a dictionary===
 
A ''dictionary'' is a key/value data structure, represented like this in JSON exports:
 
A ''dictionary'' is a key/value data structure, represented like this in JSON exports:
<source lang="json">
+
<syntaxhighlight lang="json">
 
{
 
{
 
   "key A": "value A",
 
   "key A": "value A",
Line 240: Line 240:  
   ...
 
   ...
 
}
 
}
</source>
+
</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 <tt>asset.AsDictionary<TKey, string>()</tt>, where <tt>TKey</tt> is replaced with the key type (usually <tt>int</tt> or <tt>string</tt>).
Line 246: Line 246:  
; Data
 
; Data
 
: A reference to the loaded data. For example, here's how to add or replace a specific entry to the above example:
 
: A reference to the loaded data. For example, here's how to add or replace a specific entry to the above example:
: <source lang="C#">
+
: <syntaxhighlight lang="C#">
 
public void Edit<T>(IAssetData asset)
 
public void Edit<T>(IAssetData asset)
 
{
 
{
Line 252: Line 252:  
   editor.Data["Key C"] = "Value C";
 
   editor.Data["Key C"] = "Value C";
 
}
 
}
</source>
+
</syntaxhighlight>
    
===Edit an image===
 
===Edit an image===
Line 262: Line 262:  
; PatchImage
 
; PatchImage
 
: Edit or replace part of the image. This is basically a copy & paste operation, so the source texture is applied over the loaded texture. For example:
 
: Edit or replace part of the image. This is basically a copy & paste operation, so the source texture is applied over the loaded texture. For example:
: <source lang="C#">
+
: <syntaxhighlight lang="C#">
 
public void Edit<T>(IAssetData asset)
 
public void Edit<T>(IAssetData asset)
 
{
 
{
Line 270: Line 270:  
   editor.PatchImage(sourceImage, targetArea: new Rectangle(300, 100, 200, 200));
 
   editor.PatchImage(sourceImage, targetArea: new Rectangle(300, 100, 200, 200));
 
}
 
}
</source>
+
</syntaxhighlight>
    
: Available method arguments:
 
: Available method arguments:
Line 295: Line 295:  
; ExtendImage
 
; ExtendImage
 
: Extend the image if needed to fit the given size. Note that '''this is an expensive operation''', creates a new texture instance, and extending a spritesheet horizontally may cause game errors or bugs. For example:
 
: Extend the image if needed to fit the given size. Note that '''this is an expensive operation''', creates a new texture instance, and extending a spritesheet horizontally may cause game errors or bugs. For example:
: <source lang="C#">
+
: <syntaxhighlight lang="C#">
 
public void Edit<T>(IAssetData asset)
 
public void Edit<T>(IAssetData asset)
 
{
 
{
Line 303: Line 303:  
   editor.ExtendImage(minWidth: editor.Data.Width, minHeight: 1000);
 
   editor.ExtendImage(minWidth: editor.Data.Width, minHeight: 1000);
 
}
 
}
</source>
+
</syntaxhighlight>
 
: Available method arguments:
 
: Available method arguments:
 
: {| class="wikitable"
 
: {| class="wikitable"
Line 325: Line 325:  
; PatchMap
 
; PatchMap
 
: Edit or replace part of the map. This is basically a copy & paste operation, so the source map is applied over the loaded map. For example:
 
: Edit or replace part of the map. This is basically a copy & paste operation, so the source map is applied over the loaded map. For example:
: <source lang="C#">
+
: <syntaxhighlight lang="C#">
 
public void Edit<T>(IAssetData asset)
 
public void Edit<T>(IAssetData asset)
 
{
 
{
Line 333: Line 333:  
   editor.PatchMap(sourceMap, targetArea: new Rectangle(30, 10, 20, 20));
 
   editor.PatchMap(sourceMap, targetArea: new Rectangle(30, 10, 20, 20));
 
}
 
}
</source>
+
</syntaxhighlight>
    
: Available method arguments:
 
: Available method arguments:
Line 358: Line 358:     
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|<tt>PathUtilities</tt>]] and compare case-insensitively. For example:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
string dialoguePrefix = PathUtilities.NormalizePath(@"Characters\Dialogue\");
 
string dialoguePrefix = PathUtilities.NormalizePath(@"Characters\Dialogue\");
 
bool isDialogue = asset.AssetName.StartsWith(dialoguePrefix, StringComparison.OrdinalIgnoreCase);
 
bool isDialogue = asset.AssetName.StartsWith(dialoguePrefix, StringComparison.OrdinalIgnoreCase);
</source>
+
</syntaxhighlight>
    
===Cache invalidation===
 
===Cache invalidation===
Line 369: Line 369:     
Typically you'll invalidate a specific asset key:
 
Typically you'll invalidate a specific asset key:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
helper.Content.InvalidateCache("Data/ObjectInformation");
 
helper.Content.InvalidateCache("Data/ObjectInformation");
</source>
+
</syntaxhighlight>
    
You can also invalidate assets matching a lambda:
 
You can also invalidate assets matching a lambda:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
helper.Content.InvalidateCache(asset => asset.DataType == typeof(Texture2D) && asset.AssetNameEquals("Data/ObjectInformation"));
 
helper.Content.InvalidateCache(asset => asset.DataType == typeof(Texture2D) && asset.AssetNameEquals("Data/ObjectInformation"));
</source>
+
</syntaxhighlight>
    
===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 <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:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
public class ModEntry : Mod
 
public class ModEntry : Mod
 
{
 
{
Line 391: Line 391:  
     }
 
     }
 
}
 
}
</source>
+
</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 <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.
Line 399: Line 399:     
You can get a patch helper for arbitrary data. For example, this loads two map files and merges them:
 
You can get a patch helper for arbitrary data. For example, this loads two map files and merges them:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
Map farm = this.Helper.Content.Load<Map>("assets/farm.tmx", ContentSource.ModFolder);
 
Map farm = this.Helper.Content.Load<Map>("assets/farm.tmx", ContentSource.ModFolder);
 
Map islands = this.Helper.Content.Load<Map>("assets/islands.tmx", ContentSource.ModFolder);
 
Map islands = this.Helper.Content.Load<Map>("assets/islands.tmx", ContentSource.ModFolder);
Line 407: Line 407:  
   .AsMap()
 
   .AsMap()
 
   .PatchMap(source: islands, targetArea: new Rectangle(0, 26, 56, 49));
 
   .PatchMap(source: islands, targetArea: new Rectangle(0, 26, 56, 49));
</source>
+
</syntaxhighlight>
    
See [[#Edit a game asset|''edit a game asset'']] for a description of the available patch helpers.
 
See [[#Edit a game asset|''edit a game asset'']] for a description of the available patch helpers.
114

edits

Navigation menu