Changes

Jump to navigation Jump to search
no edit summary
Line 157: Line 157:     
<syntaxhighlight lang="c#">
 
<syntaxhighlight lang="c#">
public class ModEntry : Mod
+
internal sealed class ModEntry : Mod
 
{
 
{
 
     /// <inheritdoc/>
 
     /// <inheritdoc/>
Line 183: Line 183:  
Providing a new asset is exactly like replacing an existing one (see previous sections). 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 sections). For example, this code adds a new dialogue file for a custom NPC:
 
<syntaxhighlight lang="c#">
 
<syntaxhighlight lang="c#">
public class ModEntry : Mod
+
internal sealed class ModEntry : Mod
 
{
 
{
 
     /// <inheritdoc/>
 
     /// <inheritdoc/>
Line 227: Line 227:  
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:
 
<syntaxhighlight lang="c#">
 
<syntaxhighlight lang="c#">
public class ModEntry : Mod
+
internal sealed class ModEntry : Mod
 
{
 
{
 
     /// <inheritdoc/>
 
     /// <inheritdoc/>
Line 240: Line 240:  
     private void OnAssetRequested(object sender, AssetRequestedEventArgs e)
 
     private void OnAssetRequested(object sender, AssetRequestedEventArgs e)
 
     {
 
     {
         if (e.NameWithoutLocale.IsEquivalentTo("Data/ObjectInformation"))
+
         if (e.NameWithoutLocale.IsEquivalentTo("Data/Objects"))
 
         {
 
         {
 
             e.Edit(asset =>
 
             e.Edit(asset =>
 
             {
 
             {
                 var data = asset.AsDictionary<int, string>().Data;
+
                 var data = asset.AsDictionary<string, ObjectData>().Data;
   −
                 foreach (int itemID in data.Keys.ToArray())
+
                 foreach ((string itemID, ObjectData itemData) in data)
 
                 {
 
                 {
                     string[] fields = data[itemID].Split('/');
+
                     itemData.Price *= 2;
                    fields[1] = (int.Parse(fields[1]) * 2).ToString();
  −
                    data[itemID] = string.Join("/", fields);
   
                 }
 
                 }
 
             });
 
             });
Line 257: Line 255:  
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  −
Note that you'll run into errors if you try to edit the collection you're iterating over. The <samp>ToArray()</samp> avoids that by iterating over a copy of the keys instead.
      
The <samp>IAssetData asset</samp> argument from the <samp>Edit</samp> method has some helpers to make editing data easier, documented below. (See IntelliSense for more info.)
 
The <samp>IAssetData asset</samp> argument from the <samp>Edit</samp> method has some helpers to make editing data easier, documented below. (See IntelliSense for more info.)
Line 286: Line 282:  
:: 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:
 
:: <syntaxhighlight lang="C#">
 
:: <syntaxhighlight lang="C#">
public void Edit<T>(IAssetData asset)
+
 
{
+
    /// <inheritdoc cref="IContentEvents.AssetRequested"/>
  var editor = asset.AsDictionary<string, string>();
+
    /// <param name="sender">The event sender.</param>
  editor.Data["Key C"] = "Value C";
+
    /// <param name="e">The event data.</param>
}
+
    private void OnAssetRequested(object sender, AssetRequestedEventArgs e)
 +
    {
 +
        if (e.NameWithoutLocale.IsEquivalentTo("Location/Of/The/Asset"))
 +
        {
 +
            e.Edit(asset =>
 +
            {
 +
                var editor = asset.AsDictionary<string, string>();
 +
                editor.Data["Key C"] = "Value C";
 +
            });
 +
        }
 +
    }
 
</syntaxhighlight>
 
</syntaxhighlight>
   Line 302: Line 308:  
:: 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:
 
:: <syntaxhighlight lang="C#">
 
:: <syntaxhighlight lang="C#">
public void Edit<T>(IAssetData asset)
+
 
{
+
    /// <inheritdoc cref="IContentEvents.AssetRequested"/>
  var editor = asset.AsImage();
+
    /// <param name="sender">The event sender.</param>
 
+
    /// <param name="e">The event data.</param>
  Texture2D sourceImage = this.Helper.ModContent.Load<Texture2D>("custom-texture.png");
+
    private void OnAssetRequested(object sender, AssetRequestedEventArgs e)
  editor.PatchImage(sourceImage, targetArea: new Rectangle(300, 100, 200, 200));
+
    {
}
+
        if (e.NameWithoutLocale.IsEquivalentTo("Location/Of/The/Asset"))
 +
        {
 +
            e.Edit(asset =>
 +
            {
 +
                  var editor = asset.AsImage();
 +
                  IRawTextureData sourceImage = this.Helper.ModContent.Load<IRawTextureData>("custom-texture.png");
 +
                  editor.PatchImage(sourceImage, targetArea: new Rectangle(300, 100, 200, 200));
 +
            });
 +
        }
 +
    }
 
</syntaxhighlight>
 
</syntaxhighlight>
   Line 329: Line 344:  
| ''(optional)'' How the image should be patched. The possible values...
 
| ''(optional)'' How the image should be patched. The possible values...
 
* <samp>PatchMode.Replace</samp> (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;
* <samp>PatchMode.Overlay</samp>: 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 transparent or semi-transparent pixels.
 
|}
 
|}
    
:; 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 '''resizing the image is an expensive operation''', creates a new texture instance, and extending a spritesheet horizontally may cause game errors or bugs. For example:
 
:: <syntaxhighlight lang="C#">
 
:: <syntaxhighlight lang="C#">
public void Edit<T>(IAssetData asset)
+
 
 +
e.Edit(asset =>
 
{
 
{
 
   var editor = asset.AsImage();
 
   var editor = asset.AsImage();
Line 341: Line 357:  
   // make sure the image is at least 1000px high
 
   // make sure the image is at least 1000px high
 
   editor.ExtendImage(minWidth: editor.Data.Width, minHeight: 1000);
 
   editor.ExtendImage(minWidth: editor.Data.Width, minHeight: 1000);
}
+
});
 
</syntaxhighlight>
 
</syntaxhighlight>
 
:: Available method arguments:
 
:: Available method arguments:
Line 365: Line 381:  
:: 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:
 
:: <syntaxhighlight lang="C#">
 
:: <syntaxhighlight lang="C#">
public void Edit<T>(IAssetData asset)
+
e.Edit(asset =>
 
{
 
{
 
   var editor = asset.AsMap();
 
   var editor = asset.AsMap();
Line 371: Line 387:  
   Map sourceMap = this.Helper.ModContent.Load<Map>("custom-map.tmx");
 
   Map sourceMap = this.Helper.ModContent.Load<Map>("custom-map.tmx");
 
   editor.PatchMap(sourceMap, targetArea: new Rectangle(30, 10, 20, 20));
 
   editor.PatchMap(sourceMap, targetArea: new Rectangle(30, 10, 20, 20));
}
+
});
 
</syntaxhighlight>
 
</syntaxhighlight>
   Line 405: Line 421:  
:: Extend the map if needed to fit the given size. Note that '''this is an expensive operation''' and resizes the map in-place. For example:
 
:: Extend the map if needed to fit the given size. Note that '''this is an expensive operation''' and resizes the map in-place. For example:
 
:: <syntaxhighlight lang="C#">
 
:: <syntaxhighlight lang="C#">
public void Edit<T>(IAssetData asset)
+
 
 +
e.Edit(asset =>
 
{
 
{
 
   var editor = asset.AsMap();
 
   var editor = asset.AsMap();
    
   // make sure the map is at least 256 tiles high
 
   // make sure the map is at least 256 tiles high
   editor.ExtendImage(minHeight: 256);
+
   editor.ExtendMap(minHeight: 256);
}
+
});
 
</syntaxhighlight>
 
</syntaxhighlight>
 
:: Available method arguments:
 
:: Available method arguments:
Line 459: Line 476:  
IRawTextureData image = this.Helper.ModContent.Load<IRawTextureData>("assets/image.png");
 
IRawTextureData image = this.Helper.ModContent.Load<IRawTextureData>("assets/image.png");
   −
for (int i = 0; i < image.Data.Length; i++)
+
int pixelCount = image.Width * image.Height;
 +
for (int i = 0; i < pixelCount; i++)
 
{
 
{
 
     Color color = image.Data[i];
 
     Color color = image.Data[i];
Line 469: Line 487:  
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
(Note: while SMAPI's implementation of IRawTextureData is exactly as long as it needs to be, this may not be the case for implementations of IRawTextureData from mods.)
    
===Compare asset names===
 
===Compare asset names===
Line 493: Line 513:  
===Cache invalidation===
 
===Cache invalidation===
 
You can reload an asset by invalidating it from the cache. It will be reloaded next time the game requests it (and mods will have another chance to intercept it), and SMAPI will automatically update references to the asset in many cases. For example, this lets you change what clothes an NPC is wearing (by invalidating their cached sprites or portraits).
 
You can reload an asset by invalidating it from the cache. It will be reloaded next time the game requests it (and mods will have another chance to intercept it), and SMAPI will automatically update references to the asset in many cases. For example, this lets you change what clothes an NPC is wearing (by invalidating their cached sprites or portraits).
 +
 +
Please be aware that in some cases a localized version of the asset will be cached and simply invalidating the default asset will not work for any language other than english.
    
Reloading assets is fairly expensive, so use this API judiciously to avoid impacting game performance. Definitely don't do this every update tick.
 
Reloading assets is fairly expensive, so use this API judiciously to avoid impacting game performance. Definitely don't do this every update tick.
2

edits

Navigation menu