Line 56: |
Line 56: |
| <li>Add a file named <tt>manifest.json</tt> to your project.</li> | | <li>Add a file named <tt>manifest.json</tt> to your project.</li> |
| <li>Paste this code into the file (replacing the <tt><...></tt> placeholders): | | <li>Paste this code into the file (replacing the <tt><...></tt> placeholders): |
− | <pre lang="json"> | + | <source lang="json"> |
| { | | { |
| "Name": "<your project name>", | | "Name": "<your project name>", |
Line 70: |
Line 70: |
| "EntryDll": "<your project name>.dll" | | "EntryDll": "<your project name>.dll" |
| } | | } |
− | </pre></li> | + | </source></li> |
| </ol> | | </ol> |
| | | |
Line 81: |
Line 81: |
| <li>Add a C# class file called <tt>ModEntry.cs</tt> to your project.</li> | | <li>Add a C# class file called <tt>ModEntry.cs</tt> to your project.</li> |
| <li>Put this code in the file (replace <tt><your project name></tt> with the name of your project): | | <li>Put this code in the file (replace <tt><your project name></tt> with the name of your project): |
− | <pre lang="c#"> | + | <source lang="c#"> |
| using System; | | using System; |
| using Microsoft.Xna.Framework; | | using Microsoft.Xna.Framework; |
Line 113: |
Line 113: |
| } | | } |
| } | | } |
− | </pre></li> | + | </source></li> |
| </ol> | | </ol> |
| | | |
Line 131: |
Line 131: |
| SMAPI publishes several C# events that tell you when something happens. For example, if you want to do something after the player loads their save, you can add this to your <tt>Entry</tt> method: | | SMAPI publishes several C# events that tell you when something happens. For example, if you want to do something after the player loads their save, you can add this to your <tt>Entry</tt> method: |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| SaveEvents.AfterLoad += this.ReceiveAfterLoad; | | SaveEvents.AfterLoad += this.ReceiveAfterLoad; |
− | </pre> | + | </source> |
| | | |
| Then declare a method like this. (The <tt>EventArgs e</tt> argument will often provide more details about what happened, if there are any.) | | Then declare a method like this. (The <tt>EventArgs e</tt> argument will often provide more details about what happened, if there are any.) |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| /// <summmary>The event handler called after the player loads their save.</summary> | | /// <summmary>The event handler called after the player loads their save.</summary> |
| /// <param name="sender">The event sender.</param> | | /// <param name="sender">The event sender.</param> |
Line 146: |
Line 146: |
| this.Monitor.Log("Everything in the world is ready to interact with at this point."); | | this.Monitor.Log("Everything in the world is ready to interact with at this point."); |
| } | | } |
− | </pre> | + | </source> |
| | | |
| Here are the available events: | | Here are the available events: |
Line 324: |
Line 324: |
| You can set defaults directly: | | You can set defaults directly: |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| class ModConfig | | class ModConfig |
| { | | { |
Line 330: |
Line 330: |
| public float ExampleFloat { get; set; } = 0.5; | | public float ExampleFloat { get; set; } = 0.5; |
| } | | } |
− | </pre> | + | </source> |
| | | |
| ...or with a constructor: | | ...or with a constructor: |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| class ModConfig | | class ModConfig |
| { | | { |
Line 346: |
Line 346: |
| } | | } |
| } | | } |
− | </pre></li> | + | </source></li> |
| | | |
| <li>In your <tt>ModEntry::Entry</tt> method, add this line to read the config options: | | <li>In your <tt>ModEntry::Entry</tt> method, add this line to read the config options: |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| ModConfig config = helper.ReadConfig<ModConfig>(); | | ModConfig config = helper.ReadConfig<ModConfig>(); |
− | </pre> | + | </source> |
| </li> | | </li> |
| </ol> | | </ol> |
Line 372: |
Line 372: |
| <ul> | | <ul> |
| <li>Load an image from your mod folder (from an <tt>assets</tt> subfolder in this example): | | <li>Load an image from your mod folder (from an <tt>assets</tt> subfolder in this example): |
− | <pre lang="c#"> | + | <source lang="c#"> |
| // load an XNB file | | // load an XNB file |
| var texture = helper.Content.Load<Texture2D>(@"assets\texture.xnb", ContentSource.ModFolder); | | var texture = helper.Content.Load<Texture2D>(@"assets\texture.xnb", ContentSource.ModFolder); |
Line 378: |
Line 378: |
| // load a PNG file | | // load a PNG file |
| var texture = helper.Content.Load<Texture2D>(@"assets\texture.png", ContentSource.ModFolder); | | var texture = helper.Content.Load<Texture2D>(@"assets\texture.png", ContentSource.ModFolder); |
− | </pre></li> | + | </source></li> |
| | | |
| <li>Load an asset from game's content folder: | | <li>Load an asset from game's content folder: |
− | <pre lang="c#"> | + | <source lang="c#"> |
| var data = helper.Content.Load<Dictionary<int, string>>(@"Data\ObjectInformation.xnb", ContentSource.GameContent); | | var data = helper.Content.Load<Dictionary<int, string>>(@"Data\ObjectInformation.xnb", ContentSource.GameContent); |
− | </pre></li> | + | </source></li> |
| | | |
| <li>Load a custom tilesheet for a map: | | <li>Load a custom tilesheet for a map: |
− | <pre lang="c#"> | + | <source lang="c#"> |
| tilesheet.ImageSource = helper.Content.GetActualAssetKey(@"assets\tilesheet.png"); | | tilesheet.ImageSource = helper.Content.GetActualAssetKey(@"assets\tilesheet.png"); |
− | </pre></li> | + | </source></li> |
| </ul> | | </ul> |
| | | |
Line 396: |
Line 396: |
| Your mod can write messages to the console window and log file using the monitor. For example, this code: | | Your mod can write messages to the console window and log file using the monitor. For example, this code: |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| this.Monitor.Log("a trace message", LogLevel.Trace); | | this.Monitor.Log("a trace message", LogLevel.Trace); |
| this.Monitor.Log("a debug message", LogLevel.Debug); | | this.Monitor.Log("a debug message", LogLevel.Debug); |
Line 402: |
Line 402: |
| this.Monitor.Log("a warning message", LogLevel.Warn); | | this.Monitor.Log("a warning message", LogLevel.Warn); |
| this.Monitor.Log("an error message", LogLevel.Error); | | this.Monitor.Log("an error message", LogLevel.Error); |
− | </pre> | + | </source> |
| | | |
| will log something like this: | | will log something like this: |
Line 425: |
Line 425: |
| Here are a few examples of what this lets you do: | | Here are a few examples of what this lets you do: |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| // did you pet your pet today? | | // did you pet your pet today? |
| bool wasPet = this.Helper.Reflection.GetPrivateValue<bool>(pet, "wasPetToday"); | | bool wasPet = this.Helper.Reflection.GetPrivateValue<bool>(pet, "wasPetToday"); |
Line 437: |
Line 437: |
| if(Game1.currentLocation is MineShaft) | | if(Game1.currentLocation is MineShaft) |
| this.Helper.Reflection.GetPrivateField<Random>(Game1.currentLocation, "mineRandom").SetValue(new Random()); | | this.Helper.Reflection.GetPrivateField<Random>(Game1.currentLocation, "mineRandom").SetValue(new Random()); |
− | </pre> | + | </source> |
| | | |
| This works with static or instance fields/methods, caches the reflection to improve performance, and will throw useful errors automatically when reflection fails. | | This works with static or instance fields/methods, caches the reflection to improve performance, and will throw useful errors automatically when reflection fails. |
Line 443: |
Line 443: |
| If you need to do more, you can also switch to C#'s underlying reflection API: | | If you need to do more, you can also switch to C#'s underlying reflection API: |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| FieldInfo field = this.Helper.Reflection.GetPrivateField<string>(…).FieldInfo; | | FieldInfo field = this.Helper.Reflection.GetPrivateField<string>(…).FieldInfo; |
| MethodInfo method = this.Helper.Reflection.GetPrivateMethod(…).MethodInfo; | | MethodInfo method = this.Helper.Reflection.GetPrivateMethod(…).MethodInfo; |
− | </pre> | + | </source> |
| | | |
| ===Mod registry=== | | ===Mod registry=== |
| Your mod can get information about loaded mods, or check if a particular mod is loaded. (All mods are loaded by the time your mod's <tt>Entry(…)</tt> method is called.) | | Your mod can get information about loaded mods, or check if a particular mod is loaded. (All mods are loaded by the time your mod's <tt>Entry(…)</tt> method is called.) |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| // check if a mod is loaded | | // check if a mod is loaded |
| bool isLoaded = this.Helper.ModRegistry.IsLoaded("UniqueModID"); | | bool isLoaded = this.Helper.ModRegistry.IsLoaded("UniqueModID"); |
Line 460: |
Line 460: |
| // get manifest info for all loaded mods | | // get manifest info for all loaded mods |
| foreach(IManifest manifest in this.Helper.ModRegistry.GetAll()) { … } | | foreach(IManifest manifest in this.Helper.ModRegistry.GetAll()) { … } |
− | </pre> | + | </source> |
| | | |
| ==Final considerations== | | ==Final considerations== |
Line 471: |
Line 471: |
| <li>Use <tt>Path.Combine</tt> to build file paths, don't hardcode path separators since they won't work on all platforms. | | <li>Use <tt>Path.Combine</tt> to build file paths, don't hardcode path separators since they won't work on all platforms. |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| // ✘ Don't do this! It will crash on Linux/Mac. | | // ✘ Don't do this! It will crash on Linux/Mac. |
| string path = helper.DirectoryPath + "\assets\asset.xnb"; | | string path = helper.DirectoryPath + "\assets\asset.xnb"; |
Line 477: |
Line 477: |
| // ✓ This is OK | | // ✓ This is OK |
| string path = Path.Combine(helper.DirectoryPath, "assets", "asset.xnb"); | | string path = Path.Combine(helper.DirectoryPath, "assets", "asset.xnb"); |
− | </pre></li> | + | </source></li> |
| | | |
| <li>Use <tt>helper.DirectoryPath</tt>, don't try to determine the mod path yourself. | | <li>Use <tt>helper.DirectoryPath</tt>, don't try to determine the mod path yourself. |
| | | |
− | <pre lang="c#"> | + | <source lang="c#"> |
| // ✘ Don't do this! It will crash if SMAPI rewrites the assembly (e.g. to update or crossplatform it). | | // ✘ Don't do this! It will crash if SMAPI rewrites the assembly (e.g. to update or crossplatform it). |
| string modFolder = Assembly.GetCallingAssembly().Location; | | string modFolder = Assembly.GetCallingAssembly().Location; |
Line 487: |
Line 487: |
| // ✓ This is OK | | // ✓ This is OK |
| string modFolder = helper.DirectoryPath; | | string modFolder = helper.DirectoryPath; |
− | </pre></li> | + | </source></li> |
| </ol> | | </ol> |
| | | |