Changes

Jump to navigation Jump to search
Line 90: Line 90:  
> OK, set your money to 5000.
 
> OK, set your money to 5000.
 
</pre>
 
</pre>
  −
===Content===
  −
====Read assets====
  −
You can read custom images or maps to use from your mod folder or game content, with support for <tt>.xnb</tt>, <tt>.png</tt>, and <tt>.tbin</tt> files.
  −
  −
Example usage:
  −
<ul>
  −
<li>Read an image from your mod folder (from an <tt>assets</tt> subfolder in this example):
  −
<source lang="c#">
  −
// read an XNB file
  −
var texture = helper.Content.Load<Texture2D>(@"assets\texture.xnb", ContentSource.ModFolder);
  −
  −
// read a PNG file
  −
var texture = helper.Content.Load<Texture2D>(@"assets\texture.png", ContentSource.ModFolder);
  −
</source></li>
  −
  −
<li>Read a map from your mod folder (will automatically fix tilesheet references):
  −
<source lang="c#">
  −
Map map = helper.Content.Load<Map>(@"assets\map.tbin", ContentSource.ModFolder);
  −
</source></li>
  −
  −
<li>Read a new tilesheet for a map from your mod folder:
  −
<source lang="c#">
  −
tilesheet.ImageSource = helper.Content.GetActualAssetKey(@"assets\tilesheet.png", ContentSource.ModFolder);
  −
</source></li>
  −
  −
<li>Read an asset from the game's content folder:
  −
<source lang="c#">
  −
var data = helper.Content.Load<Dictionary<int, string>>(@"Data\ObjectInformation.xnb", ContentSource.GameContent);
  −
</source></li>
  −
</ul>
  −
  −
Notes:
  −
* Don't call <tt>content.Load<T></tt> in draw code for performance reasons, since drawing happens ≈60 times per second. Instead, load your content ahead of time and reuse it.
  −
* When loading a mod file, it's automatically relative to your mod folder. SMAPI won't accept an absolute file path.
  −
* Don't worry about which path separators you use; SMAPI will normalise them automatically.
  −
  −
====Edit assets====
  −
You can edit any texture or data loaded by the game (often called 'XNB data') 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. The <tt>Edit&lt;T&gt;</tt> method receives a helper for editing various data types.
  −
  −
Here are a few examples:
  −
<ul>
  −
<li>This mod adds a new item (see [[Modding:Object data]]). '''Note:''' it's better to use mod frameworks like [https://www.nexusmods.com/stardewvalley/mods/1720/ Json Assets] for custom items, to avoid dealing with save issues and ID collisions.
  −
<source lang="C#">
  −
public class ModEntry : Mod, IAssetEditor
  −
{
  −
    /// <summary>Get whether this instance can edit the given asset.</summary>
  −
    /// <param name="asset">Basic metadata about the asset being loaded.</param>
  −
    public bool CanEdit<T>(IAssetInfo asset)
  −
    {
  −
        return asset.AssetNameEquals(@"Data\ObjectInformation");
  −
    }
  −
  −
    /// <summary>Edit a matched asset.</summary>
  −
    /// <param name="asset">A helper which encapsulates metadata about an asset and enables changes to it.</param>
  −
    public void Edit<T>(IAssetData asset)
  −
    {
  −
        int id = ...;
  −
        asset
  −
            .AsDictionary<int, string>()
  −
            .Set(id, "Blood Rose/40/10/Basic -81/Blood Rose/Not the prettiest flower, but the leaves make a good salad.");
  −
    }
  −
}
  −
</source>
  −
</li>
  −
<li>This mod lets crops grow in any season (example only, doesn't handle edge cases):
  −
<source lang="c#">
  −
public class ModEntry : Mod, IAssetEditor
  −
{
  −
    /// <summary>Get whether this instance can edit the given asset.</summary>
  −
    /// <param name="asset">Basic metadata about the asset being loaded.</param>
  −
    public bool CanEdit<T>(IAssetInfo asset)
  −
    {
  −
        return asset.AssetNameEquals(@"Data\Crops");
  −
    }
  −
  −
    /// <summary>Edit a matched asset.</summary>
  −
    /// <param name="asset">A helper which encapsulates metadata about an asset and enables changes to it.</param>
  −
    public void Edit<T>(IAssetData asset)
  −
    {
  −
        asset
  −
            .AsDictionary<int, string>()
  −
            .Set((id, data) =>
  −
            {
  −
                string[] fields = data.Split('/');
  −
                fields[1] = "spring summer fall winter";
  −
                return string.Join("/", fields);
  −
            });
  −
    }
  −
}
  −
</source>
  −
</li>
  −
<li>
  −
This code replaces part of a game image:
  −
<source lang="c#">
  −
public class ModEntry : Mod, IAssetEditor
  −
{
  −
    /// <summary>Get whether this instance can edit the given asset.</summary>
  −
    /// <param name="asset">Basic metadata about the asset being loaded.</param>
  −
    public bool CanEdit<T>(IAssetInfo asset)
  −
    {
  −
        return asset.AssetNameEquals(@"Portraits\Abigail");
  −
    }
  −
  −
    /// <summary>Edit a matched asset.</summary>
  −
    /// <param name="asset">A helper which encapsulates metadata about an asset and enables changes to it.</param>
  −
    public void Edit<T>(IAssetData asset)
  −
    {
  −
        Texture2D customTexture = this.Helper.Content.Load<Texture2D>("custom-texture.png", ContentSource.ModFolder);
  −
        asset
  −
            .AsImage()
  −
            .PatchImage(customTexture, targetArea: new Rectangle(300, 100, 200, 200));
  −
    }
  −
</source>
  −
</li>
  −
</ul>
  −
  −
For more advanced scenarios, you can inject multiple <tt>IAssetEditor</tt> instances instead. When you inject a new editor, SMAPI will automatically reload all game assets so it can intercept them. (This is resource-intensive when done outside your <tt>Entry</tt> method, so avoid adding editors unnecessarily.)
  −
<source lang="c#">
  −
public class ModEntry : Mod
  −
{
  −
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
  −
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
  −
    public override void Entry(IModHelper helper)
  −
    {
  −
        helper.Content.AssetEditors.Add(new MyCropEditor());
  −
    }
  −
}
  −
</source>
  −
  −
====Inject assets====
  −
You can inject new assets for the game to use (e.g. to add dialogue for a custom NPC). '''To edit existing assets, you should use [[#Edit assets|asset editors]] instead.''' If multiple loaders match the same asset, SMAPI will show an error and use none of them.
  −
  −
In most cases, you can just implement <tt>IAssetLoader</tt> in your <tt>Mod</tt> class to do it. This adds two methods: <tt>CanLoad&lt;T&gt;</tt> returns whether the mod can load a particular asset, and <tt>Load&lt;T&gt;</tt> provides the asset data. For example, this code adds a new dialogue file for a custom NPC.
  −
<source lang="c#">
  −
public class ModEntry : Mod, IAssetLoader
  −
{
  −
    /// <summary>Get whether this instance can load the initial version of the given asset.</summary>
  −
    /// <param name="asset">Basic metadata about the asset being loaded.</param>
  −
    public bool CanLoad<T>(IAssetInfo asset)
  −
    {
  −
        return asset.AssetNameEquals(@"Characters\Dialogue\John");
  −
    }
  −
  −
    /// <summary>Load a matched asset.</summary>
  −
    /// <param name="asset">Basic metadata about the asset being loaded.</param>
  −
    public T Load<T>(IAssetInfo asset)
  −
    {
  −
        return (T)(object)new Dictionary<string, string> // (T)(object) is a trick to cast anything to T if we know it's compatible
  −
        {
  −
            ["Introduction"] = "Hi there! My name is Jonathan."
  −
        };
  −
    }
  −
}
  −
</source>
  −
  −
For more advanced scenarios, you can inject multiple <tt>IAssetLoader</tt> instances instead. When you inject a new loader, SMAPI will automatically reload all game assets so it can intercept them. (This is resource-intensive when done outside your <tt>Entry</tt> method, so avoid adding loaders unnecessarily.)
  −
<source lang="c#">
  −
public class ModEntry : Mod
  −
{
  −
    /// <summary>The mod entry point, called after the mod is first loaded.</summary>
  −
    /// <param name="helper">Provides simplified APIs for writing mods.</param>
  −
    public override void Entry(IModHelper helper)
  −
    {
  −
        helper.Content.AssetLoaders.Add(new MyDialogueLoader());
  −
    }
  −
}
  −
</source>
  −
  −
====Reload assets====
  −
'''Caution:''' reloading assets is fairly expensive, so use this API judiciously to avoid impacting game performance.
  −
  −
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 reload it automatically if it's a core asset that's kept in memory by the game. For example, this lets you change what clothes an NPC is wearing (by invalidating their cached sprites or portraits).
  −
  −
Typically you'll invalidate a specific asset key:
  −
<source lang="c#">
  −
helper.Content.InvalidateCache(@"Data\ObjectInformation.xnb"); // path separators and capitalisation don't matter
  −
</source>
  −
  −
You can also invalidate assets matching a lambda:
  −
<source lang="c#">
  −
helper.Content.InvalidateCache(asset => asset.DataType == typeof(Texture2D) && asset.AssetNameEquals(@"Data\ObjectInformation.xnb"));
  −
</source>
      
===Content packs===
 
===Content packs===
translators
8,446

edits

Navigation menu