--- title: "Blade and Sorcery: A quick intro to C# Script Modding" --- # Blade and Sorcery: A quick intro to C# Script Modding [TOC] ## Get Visual Studio - https://visualstudio.microsoft.com/downloads/ - Get the community edition (free) ## Creating the project 1. Open Visual Studio, and select _Create a new project_. ![](https://i.imgur.com/rfUAHQh.png) 2. Search for the template _Class Library (<a href="https://dreamix.eu/blog/tech/net/the-future-of-net-in-2023">.NET Frameworks</a>)_. ![](https://i.imgur.com/QTOWaoa.png) 3. Name your project. ![](https://i.imgur.com/tKTQJfQ.png) :::danger Make sure that _Framework_ is set to `.NET Framework 4.7.2`. ::: 4. Hit Create! ### Visual Studio UI A quick rundown of the VS UI: - Code window ![](https://i.imgur.com/JXAKayD.png) - Write your code - Solution Explorer ![](https://i.imgur.com/6Z8mjCF.png) - Add references - Right click the References section - Configure your project - Click the project name "MyNewMod", then the spanner icon in the Properties pane below - Add classes - Right click > Add > Class... - Output / Errors ![](https://i.imgur.com/8EpuGde.png) - Shows you all the syntax errors in the code ## Importing the B&S SDK You'll be greeted with the following: ![](https://i.imgur.com/sohItgz.png) - In the Solution Explorer, right click References > Add Reference ![](https://i.imgur.com/FqMq0HW.png) - Click _Browse..._ ![](https://i.imgur.com/fpwe4KI.png) - Navigate to your Blade and Sorcery installation - Most commonly this is ``` C:\Program Files (x86)\Steam\steamapps\common\Blade & Sorcery\BladeAndSorcery_Data\Managed ``` - Select and add the following files: - `Assembly-CSharp.dll`: Blade and Sorcery SDK. Required. - `UnityEngine.dll`: Unity SDK. Required. - `UnityEngine.CoreModule.dll`: Core parts of Unity. Required. - _Optional:_ `UnityEngine.PhysicsModule`: Physics functions, raycasting etc. - Any other DLLs you need for your mod - Press _OK_ - In your code window, import the B&S and Unity SDKs: `ThunderRoad` and `UnityEngine`. ![](https://i.imgur.com/MxYbmRT.png) ## Writing Code There is a lot of things to learn here - too much for one guide. Here are the very basic structures for the two most common usecases - weapon modules, and spells. Skip to [Compiling](#Compiling) for how to compile your code. ### Weapon Module If you're making a weapon module, you'll want something like this: ```csharp= using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ThunderRoad; using UnityEngine; namespace MyNewMod { public class MyWeaponModule : ItemModule { public override void OnItemLoaded(Item item) { base.OnItemLoaded(item); item.gameObject.AddComponent<MyWeaponComponent>(); } } public class MyWeaponComponent : MonoBehaviour { Item item; public void Start() { item = GetComponent<Item>(); // Code here runs once when the weapon is spawned } public void Update() { // Code here runs constantly as long as the weapon exists // --- REMOVE ME --- // Some useful things: if (item.holder != null) { // Item is in a holster / weapon rack } if (item.mainHandler != null) { // Item is being held // The hand in which it is being held Debug.Log(item.mainHandler); } if (item.isFlying) { // Item is flying (from a throw or TK throw) } if (item.isPenetrating) { // Item is stabbed into something } } } } ``` ### Charged Spell ```csharp= using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ThunderRoad; using UnityEngine; namespace MyNewMod { public class MySpell : SpellCastCharge { bool isCasting; public override void Load(SpellCaster spellCaster) { base.Load(spellCaster); // Run when the spell is selected } public override void Fire(bool active) { base.Fire(active); isCasting = active; if (active) { // Spell has just started to be cast } else { // Spell has stopped being cast } } public override void UpdateCaster() { base.UpdateCaster(); if (isCasting) { // Run once per frame while the spell is being cast // --- REMOVE ME --- // Useful stuff: // The spellCaster object with info about the player Debug.Log(spellCaster); // The center of the spell casting orb Debug.Log(spellCaster.magicSource); // The caster's amount of mana Debug.Log(spellCaster.mana.currentMana); // The current charge of the spell Debug.Log(currentCharge); } } public override void Throw(Vector3 velocity) { base.Throw(velocity); // Run when the spell is thrown (think fireball / gravity throws) } public override void OnSprayStart() { base.OnSprayStart(); // Run when the spell begins to be sprayed (think lightning) } public override void OnSprayLoop() { base.OnSprayLoop(); // Run once per frame as the spell is being sprayed } public override void OnImbueCollisionStart(ref CollisionStruct collisionInstance) { base.OnImbueCollisionStart(ref collisionInstance); // Run when a weapon imbued with your spell hits something } } } ``` ## Compiling Hit Ctrl+Shift+B to compile your code. This will output a DLL in your project directory. To make my life easier, I set up a post-build event that automatically copies the DLL to my mod staging folder. 1. Select your project in the Solution Explorer pane ![](https://i.imgur.com/n6F0E6v.png) 2. Press the spanner icon (Property Pages) button in the Properties pane below ![](https://i.imgur.com/Y9eggQ4.png) 3. Head to _Build Events_ and click _Edit Post-build..._ ![](https://i.imgur.com/jUelsdt.png) 4. Add the following line, **changing the directory to your SDK BuildStaging folder**: ![](https://i.imgur.com/7w5Qzfu.png) ```batch copy /Y "$(TargetPath)" "C:\path\to\BuildStaging\Catalog\MyModName\$(TargetFileName)" ``` 5. Press Ctrl+Shift+B to build your code 6. Continue with the normal mod build process in Unity - using the B&S SDK Unity Mod Builder ![](https://i.imgur.com/p2saPZ4.png) :::warning Replace "Recall" with your mod folder name. Untick all addressable groups if you don't have any Unity assets to export. ::: ### On the BuildStaging folder Your `BuildStaging` folder is where you should be putting all of the JSON files for your mod, and where your DLL should be exported to. When you run the Mod Builder from Unity, it will copy your DLLs and JSON files from that directory to the game's mod folder. The philosophy behind the `BuildStaging` folder is manifold: - Having your JSONs/DLLs inside `BuildStaging` makes them accessible by Unity - It's good to have everything in one place - JSONs, DLLs and any Unity Addressables/Assets are all located within `BuildStaging` - It means that if you accidentally wipe or reinstall Blade and Sorcery, you won't lose your work (as your work lives outside the B&S game folder) - BaS Mod Builder is able to wipe clean your B&S mods folder before building to make sure everything is fresh and up-to-date Your `BuildStaging` folder should look like this: ``` BasSDK (the B&S SDK downloaded from github) |- Assets |- BuildStaging | |- Catalog | | |- MyModName | | | |- Effects (if your mod has effects) | | | |- Items (if your mod has items) | | | |- Spells (if your mod has spells) | | | |- ... | | | |- MyModName.dll | | | |- manifest.json |- Library |- Packages |- ProjectSettings |- ... ``` If you can't find a BuildStaging folder, you can make one. All you need to make is the `BuildStaging/Catalog/MyModName` folder. When I don't want to have to re-export unchanged Unity assets, and I've only made changes to the DLL, I often just copy the DLL straight to the mod folder. This can be done through another post-build event: ```batch copy /Y "$(TargetPath)" "C:\Program Files (x86)\Steam\steamapps\common\Blade & Sorcery\BladeAndSorcery_Data\StreamingAssets\Mods\MyModName\$(TargetFileName)" ``` ## Packaging ### Mod Folder Layout The standard for B&S mod folders is as such: ``` MyModName |- Effects | |- Effect_SomeEffectName.json | |- Effect_AnotherEffectName.json |- Spells | |- Spell_FireButBetter.json | |- Spell_EnemyYeet.json |- Items | |- Item_SpellFireButBetter.json | |- Item_SpellEnemyYeet.json |- ... (other subfolders for other Catalog object types) |- MyModName.dll (the DLL built by Visual Studio) |- Container_PlayerDefault.json (required for spell mods, see below) |- manifest.json (info about your mod) ``` ## JSON Files ### What JSONs do I need? #### Weapon Mods - Item JSONs for each weapon in your mod #### Spell Mods - Spell JSONs for each spell in your mod - Spell JSONs for each spell merge in your mod - An Item JSON for each spell and spell merge JSON. Check ``` Blade & Sorcery\BladeAndSorcery_Data\StreamingAssets\Default\Bas\Items\Item_SpellFire ``` for an example. In Blade and Sorcery, spells have associated 'items' that can be inside the player's 'inventory'. - `Container_PlayerDefault.json`: Without this, the player won't have these spells in their inventory. Check ``` Blade & Sorcery\BladeAndSorcery_Data\StreamingAssets\Default\Bas\Containers ``` for an example. You pretty much just need one of these for each of your spells and merges: ``` { "referenceID": "SpellGravity", "reference": "Item", "quantity": 1, "customValues": [] }, ``` Note that the `"referenceID"` entry links to the `Item_SpellName.json` file, **not** your `Spell_Name.json` file. ## Example Spell Guides This section is a work-in-progress. ### Charge-cast Spell: Resurrection ### Imbued Spell (with crystal use): Cosmic's Implosion Imbue ### Merged Spell: Firestorm ### Sprayed Spell: Ice Spray ## Random Useful Tips / Snippets ```csharp= // List of all creatures (player / enemies) Creature.list; // List of all items Item.list; // Player creature Player.currentCreature; // Player left hand Player.currentCreature.handLeft; // Add force to an item Vector3 velocity = new Vector3(0, 10, 0); // Force of 10 upwards item.rb.AddForce(velocity, ForceMode.Impulse); item.Throw(); // IMPORTANT - flying item will not damage enemies without this // Kill an enemy creature.Kill(); // Resurrect a creature to 10 health // Player.currentCreature is the 'resurrector' creature.Resurrect(10, Player.currentCreature); ```