# Basic Guide for modding custom spells in B&S 1.0
> This guide needs you to have some basic knowledge of B&S modding. You should know how to export assets from unity and how to create manifests.
> You will need access to the vanilla jsons for this tutorial. These can be found in the B&S sdk or the pcvr version of the game.
> If you have any un-answered questions about this guide feel free to ping me in the offical B&S discord, @arandomperson17
:::warning
Currently on nomad, custom scripted spells will crash the game. Until this is fixed nomad modders will need to script a custom fix or use Jenix's spell hotfix as a dependency
:::
## General Tips
* When searching for files in file explorer use the wildcard `*` around whatever you search for to look for cases where it doesn't start with that phrase
* If you don't have one already I would suggest using software like VSCode to edit your jsons. This gives you custom colour schemes, highlighted keywords, automatic error detection and many more QoL options. This will make the experience less painful then if you were using something like notepad.
* For learning visual effects I use [Gabriel Aguiar Prod](https://www.youtube.com/@GabrielAguiarProd). I would specifically recomend [this](https://www.youtube.com/watch?v=XIAmTOLli2Q) video if you are completely new and [this](https://www.youtube.com/watch?v=XSdXvhLjVEk) video to learn how to convert between VFX Graph and Particle System
# Spell Json Setup
In 1.0 the only json you **need** for a custom spell is a Spell_SpellName.json. The Spell Json stores all the data needed for a spell to work like the charge rates, effectIds and skill data.
For the spell json I will use the Skill_SpellCastTest.json found in the spells folder as a base as it is the simplest but if you want to make a spell similar to vanilla ones you can use those as a base. I would suggest using another spell as a base if you don't plan on doing any scripting. Just don't delete any lines and there may be extra fields that I don't show in this tutorial.
### Initial Setup
```Json=2
"$type": "ThunderRoad.Skill.Spell.SpellCastTest, ThunderRoad",
"id": "SpellCastTest",
"sensitiveContent": "None",
"sensitiveFilterBehaviour": "Discard",
"version": 0,
"itemId": "Pottery_05",
"remoteDetonationId": "RemoteDetonation",
"castDescription": "{CupbendingDescriptionCast}",
"imbueDescription": null,
"slamDescription": null,
"chargeEffectId": null,
"readyEffectId": null,
"readyMinorEffectId": "",
"fingerEffectId": null,
"closeHandPoseId": null,
"openHandPoseId": null,
```
We then need to change the following things:
1. Change the type from `ThunderRoad.Skill.Spell.SpellCastTest` to `ThunderRoad.SpellCastCharge`. This is the base type for spells. If you are using a vanilla spell type as a base (like Fire or Gravity) then keep it the same. If you have a scripted spell then the first half will be the path to the scripted spell e.g. `MainNameSpace.SpellClass` and the second half will be the name of the dll.
2. If you want to make a new spell instead of editing a vanilla one then you will need to change the `"id"` to something unique.
3. You can remove the itemId and remoteDetonationId lines as they are only for the SpellCastTest type.
4. `"castDescription"` is what will show up when grabbing the spell in a skill tree. So you can write something custom here. Same with `"imbueDescription"` and `"slamDescription"`.
5. For the effectIds you will need to reference effect jsons. For now I will use vanilla effects but I will go over custom ones later. You can find the vanilla ones in the effects folder and you just need to copy their ids.
6. For the handPoseIds I will also be using vanilla handposes found in other spells.
This is what it should look like now:
```Json=2
"$type": "ThunderRoad.SpellCastCharge, ThunderRoad",
"id": "ExampleSpell",
"sensitiveContent": "None",
"sensitiveFilterBehaviour": "Discard",
"version": 0,
"castDescription": "Example Spell for a tutorial",
"imbueDescription": null,
"slamDescription": null,
"chargeEffectId": "SpellFireCharge",
"readyEffectId": "SpellFireReady",
"readyMinorEffectId": null,
"fingerEffectId": "SpellFireChargeFinger",
"closeHandPoseId": "ChargeClose",
"openHandPoseId": "ChargeOpen",
```
### Charge Stats
```Json=17
"chargeEffectCurve": {
"$type": "UnityEngine.AnimationCurve, UnityEngine.CoreModule",
"keys": [
{
"$type": "UnityEngine.Keyframe, UnityEngine.CoreModule",
"time": 0.0,
"value": 0.0,
"inTangent": 0.0,
"outTangent": 1.0,
"inWeight": 0.0,
"outWeight": 0.0,
"weightedMode": "None",
"tangentMode": 0
},
{
"$type": "UnityEngine.Keyframe, UnityEngine.CoreModule",
"time": 1.0,
"value": 1.0,
"inTangent": 1.0,
"outTangent": 0.0,
"inWeight": 0.0,
"outWeight": 0.0,
"weightedMode": "None",
"tangentMode": 0
}
],
"length": 2,
"preWrapMode": "ClampForever",
"postWrapMode": "ClampForever"
},
"orbVariationSpeed": 10.0,
"orbVariationAmount": 0.2,
"chargeSpeed": 0.5,
"allowStaffBuff": false,
"heldStaffModifiers": {
"$type": "System.Collections.Generic.Dictionary`2[[ThunderRoad.Modifier, ThunderRoad],[System.Single, mscorlib]], mscorlib",
"ChargeSpeed": 1.5
},
"chargeSpeedPerSkill": 0.05,
"grabbedFireMaxCharge": 0.5,
```
There's nothing you **have** to change here. I suggest just messing around and finding what values you enjoy the most. I will do a quick run-down of each term though.
* The effect curve allows you to control the speed at which the charge effect will grow. The curves for vanilla spells makes it so that it has a slow first half and then suddenly jumps to almost max after but this one is just linear.
* `"orbVariationSpeed"` and `"orbVariationAmount"` will introduce a random noise component to the charge effect.
* The charge speed will controls the amount of charge it gains per second and later you can decide the minimum charge to cast a spell. `"chargeSpeedPerSkill"` just increases charge speed per skill unlocked. Typically it would be around 0.25.
### Spell Use Choices
```Json=57
"endOnGrip": true,
"allowCharge": true,
"chargeMinHaptic": 0.05,
"chargeMaxHaptic": 0.3,
"handSpringMultiplier": 0.8,
"handLocomotionVelocityCorrectionMultiplier": 1.0,
"allowThrow": true,
"throwEffectId": null,
"throwMinCharge": 0.9,
"allowSpray": false,
"sprayHandPoseId": null,
"sprayMagicOffset": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"sprayHeadToFireMaxAngle": 45.0,
"sprayStopMinCharge": 0.2,
"sprayStartMinCharge": 0.5,
"gripCastEffectId": null,
"gripCastStatusEffectId": null,
"gripCastStatusDuration": 0.0,
```
Same with the charge settings there's nothing you have to change here. Depending on what you want to do you may want to disable/enable throwing or spraying your spell. I'll do another run-down of the non-obvious choices though
* The minCharge for throwing and sprayStart are what the chargeSpeed "counts up" to while the `"sprayStopMinCharge"` is the threshold where you can't spray after starting it.
* `"sprayHeadToFireMaxAngle"` controls how closely the player needs to point the spray so that it activates. A high angle makes it easier to trigger and a low one makes it harder.
* The gripCast section is used ingame for the Tier 1 merges with the body skill tree, like burning and shocking grasp. The effectId should link to the effect you want to play when it is active, the status effect is the status it should apply like `'Burning'` or `"Electrocute"` and `"gripCastStatusDuration"` will set the length of the status if it supports durations.
I'm going to fill in the choices so that it will work with throw, spray and grip using a mix of gravity and fire effects.
```Json=57
"endOnGrip": true,
"allowCharge": true,
"chargeMinHaptic": 0.05,
"chargeMaxHaptic": 0.3,
"handSpringMultiplier": 0.8,
"handLocomotionVelocityCorrectionMultiplier": 1.0,
"allowThrow": true,
"throwEffectId": "SpellFireballLaunch",
"throwMinCharge": 0.9,
"allowSpray": true,
"sprayHandPoseId": "SpellCast",
"sprayMagicOffset": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"sprayHeadToFireMaxAngle": 45.0,
"sprayStopMinCharge": 0.1,
"sprayStartMinCharge": 0.9,
"gripCastEffectId": "SpellPunchFlourishGravity",
"gripCastStatusEffectId": "Floating",
"gripCastStatusDuration": 5.0,
```
### Imbue
You can ignore this section if you don't intend on making a custom imbue.
```Json=79
"imbueEnabled": true,
"imbueAllowMetal": false,
"imbueRate": 1.0,
"imbueLossMultiplier": 1.0,
"imbueRadius": 0.2,
"imbueHitUseDamager": false,
"imbueHitMinVelocity": 4.0,
"imbueWhooshMinSpeed": 4.0,
"imbueWhooshMaxSpeed": 12.0,
"imbueWhooshHapticMultiplier": 1.0,
"imbueMetalEffectId": null,
"imbueBladeEffectId": null,
"imbueCrystalEffectId": null,
"imbueNaaEffectId": null,
"imbueTransferEffectId": null,
"imbueHitEnergySteal": 1.0,
"staffSlamRechargeDelay": 2.0,
"staffSlamMinVelocity": 4.0,
"staffSlamConsumptionMult": 1.0,
"staffSlamMaxVelocity": 15.0,
"staffSlamTipEffectId": null,
"staffSlamCollisionEffectId": null,
```
The only thing you need to change here would be to make sure that the effectIds link to actual effects and also to increase the `"imbueRate"`. Again I will go over custom effects later so for now you can just use vanilla ones. I'll go through some of the non-obvious options but you can skip it if you understand them.
```Json=79
"imbueEnabled": true,
"imbueAllowMetal": false,
"imbueRate": 50.0,
"imbueLossMultiplier": 1.0,
"imbueRadius": 0.2,
"imbueHitUseDamager": true,
"imbueHitMinVelocity": 1.0,
"imbueWhooshMinSpeed": 4.0,
"imbueWhooshMaxSpeed": 12.0,
"imbueWhooshHapticMultiplier": 1.0,
"imbueMetalEffectId": null,
"imbueBladeEffectId": "ImbueFire",
"imbueCrystalEffectId": "ImbueFire",
"imbueNaaEffectId": "FireImbueNaa",
"imbueTransferEffectId": "ImbueFireTransfer",
"imbueHitEnergySteal": 1.0,
"staffSlamRechargeDelay": 2.0,
"staffSlamMinVelocity": 4.0,
"staffSlamConsumptionMult": 1.0,
"staffSlamMaxVelocity": 15.0,
"staffSlamTipEffectId": "SpellFireStaffSlamTop",
"staffSlamCollisionEffectId": "SpellFireStaffSlamBottom",
```
### Wheel and AI
```Json=102
"wheelDisplayName": "Cup",
"hasOrder": false,
"order": 4,
"iconEffectId": "SpellOrbTest",
"aiCastType": "CastSimple",
"loopMaxDuration": 5.0,
"aiCastMinDistance": 1.0,
"aiCastMaxDistance": 20.0,
"aiCastGestureLength": 0.7,
"minMana": 5.0,
```
First the wheel. This should be fairly self-explanatory but the `"wheelDisplayName"` is what you want to show up if the user has spellNames on. The `"order"` controls what place it will take up in the wheel and the `"iconEffectId"` will control what the option looks like on the wheel.
The AI is more complicated however. If you don't care about enemies being able to use your spell you can ignore it.
* The main option is the `"aiCastType"`. This differentiates between spells like gravity and fire which would be `"CastSimple"` and spells like lightening `"CastLoop"`.
* `"loopMaxDuration"` will specify how long an NPC can spray for before stopping
* `"aiCastMinDistance"` and `"aiCastMaxDistance"` will let the NPC know what distance the spell should be used at. If there are multiple spells it can use at a certain distance the NPC will choose randomly between them.
* `"aiCastGestureLength"` controls how long the AI will wait before casting their spell after starting it.
* Based off the name I would think that `"minMana"` would act as some sort of delay or cooldown as the AI would need to regen to that value to cast but I can't find any use of it in the the code so I can't be as sure as the others.
I'll show what I've done for this section:
```Json=102
"wheelDisplayName": "Example Spell",
"hasOrder": true,
"order": 5,
"iconEffectId": "SpellOrbFire",
"aiCastType": "CastSimple",
"loopMaxDuration": 5.0,
"aiCastMinDistance": 0.5,
"aiCastMaxDistance": 2,
"aiCastGestureLength": 0.5,
"minMana": 40.0,
```
### Skill Tree Orb
```Json=112
"shardId": "Crystal_Small_01_Shard",
"prefabAddress": "Bas.Item.Misc.SkillOrb",
"meshAddress": null,
"meshSize": 1.0,
"orbLinkEffectId": "SkillTreeOrbLink",
"tier": 0,
"allowSkill": true,
"forceAllowRefund": true,
"showInTree": true,
"hideInSkillMenu": true,
"skillTreeDisplayName": "{CupbendingName}",
"description": "...What?",
"imageAddress": "",
"videoAddress": "",
"buttonSpriteSheetAddress": "",
"buttonEnabledIconAddress": "Bas.Ui.SkillTree.Icons[Fire_ButtonColor]",
"buttonDisabledIconAddress": "Bas.Ui.SkillTree.Icons[Fire_Button]",
"orbIconAddress": "Bas.Icon.Cup",
"tutorial": null,
"tutorialLocalizationId": null,
"tutorialGoal": null,
"tutorialGoalLocalizationId": null,
"costOverride": -1,
"isDefaultSkill": false,
"primarySkillTreeId": "Test",
"secondarySkillTreeId": null,
"isTierBlocker": true,
"groupPath": null,
"StaffSlamConsumption": 0.0,
"ReadyThreshold": 0.5,
"Ready": false,
"Order": null,
"IsCombinedSkill": false
```
If you don't want your spell to be on a skill tree and instead just want it to be on by default then you can set `"showInTree"` to `false` and `"isDefaultSkill"` to `true`. Otherwise, if you do want it to appear on a tree, I will step through what is needed now.
1. If you want the item representation of the spell to not just be an orb then you will need to fill in `"meshAddress"` with a valid address
2. Set the `"tier"` section to whatever one you want your spell to be on. zero is the first tier and counts up to two for the last tier on all vanilla skill trees
3. Next set `"allowSkill"` and `"showInTree"` to `true` then `"forceAllowRefund"` and `"hideInSkillMenu"` to `false`
4. You can then set the `"skillTreeDisplayName"` to whatever name you want for it and the description will be overridden by the specific ones you already set for cast, imbue and slam
5. The image/video address will refer to the guide that appears on the side of the description. You can leave it blank, export a custom video/image for your spell or use one from the base game
6. The icon section refers to the icon that appears on spell orbs I would suggest following Lynecas tutorial for making custom icons in the modding-resources channel or use base game ones.
7. `"primarySkillTreeId"` can be set to any skillTreeId you want. There is another tutorial in the modding-resources channel by Silk to make a custom skillTree and also explains cross-tree skills. If it will be a cross-tree skill then set `"IsCombinedSkill"` to `true`
8. Next you would use `"isTierBlocker"` to control whether the spell is the main skill of its tier. Even though it's possible, I suggest that you don't have multiple skills as the main skill since it bugs out the skill tree display.
This is what it should look like after doing all the required steps:
```Json=112
"shardId": "Crystal_Small_01_Shard",
"prefabAddress": "Bas.Item.Misc.SkillOrb",
"meshAddress": "Bas.Mesh.SkillTree.TierCrystal.Gravity.T1",
"meshSize": 1.0,
"orbLinkEffectId": "SkillTreeOrbLink",
"tier": 2,
"allowSkill": true,
"forceAllowRefund": false,
"showInTree": true,
"hideInSkillMenu": false,
"skillTreeDisplayName": "Example Spell",
"description": "",
"imageAddress": "",
"videoAddress": "Bas.Video.Skill.Fire",
"buttonSpriteSheetAddress": "Bas.Ui.SkillTree.Icons",
"buttonEnabledIconAddress": "Bas.Ui.SkillTree.Icons[Fire_ButtonColor]",
"buttonDisabledIconAddress": "Bas.Ui.SkillTree.Icons[Fire_Button]",
"orbIconAddress": "Bas.Ui.SkillTree.Icons[Fireball]",
"tutorial": null,
"tutorialLocalizationId": null,
"tutorialGoal": null,
"tutorialGoalLocalizationId": null,
"costOverride": -1,
"isDefaultSkill": false,
"primarySkillTreeId": "Fire",
"secondarySkillTreeId": "",
"isTierBlocker": false,
"groupPath": null,
"StaffSlamConsumption": 0.0,
"ReadyThreshold": 0.5,
"Ready": false,
"Order": null,
"IsCombinedSkill": false
```
Once you have finished with this you can put the json into a mod folder with a manifest and any custom effects you used and it should appear ingame.
# Custom Effect Jsons
Any custom effect starts by taking a vanilla effect json and changing the id and file name to your effect. Once you've done this you can start with EffectModules.
## EffectModules
A custom effect Json is built out of multiple smaller effect modules. The ones you will need to know about for this section are audio, shader, particle, mesh and light modules. Note that I won't be talking about vfx since they aren't available for nomad which is my only platform.
### Audio
In order to make a custom audio module you need to build/export an audio container into an asset bundle and then reference the audio container in the json. You can find `"EffectModuleAudio"` modules in most vanilla effects and can just copy and paste them into yours and replace the address. Make sure to take a look at the module options as well if you want to have more control over the audio.
### Particle
To make a particleEffectModule you will need to create a particle system in unity. Then make sure you child all particle systems into a parent with effectParticle component and add an EffectParticleChild component to each of them. Once you've done this, export it and similar to the audio replace the `"effectParticleAddress"` of an existing `"EffectModuleParticle"` with the address you exported the particleSystem. You don't need to worry about the module options as much with particles as the important ones are all done in the components added to the particle parent and children,
### Shader
Shaders allow you to change the surface of a material. It is typically used when making imbue effects. Shader modules can be done fully in json and typically you will just need to edit some of the colour values in order to use them.
### Mesh
Meshes can be used to generate meshes during an effect. The main use of them is in the spell orbs that pop up in the spell wheel. Most of the times though it is easier to use a particle system with a mesh renderer. This is mostly because it allows you to make edits to it much easier since you won't need to load into the game in order to see it.
When making a custom mesh effect you need to make and export a custom mesh and/or material then you replace the `"meshAddress"` and `"materialAddress"` with your own custom ones.
### Light
Lights can be used for any situation in which you think having your effect light up the surroundings will be useful. When doing a custom light effect module you just need to edit the json like the shader module. You can adjust the range and intensity using the range and intensity curves and can also change the colours over time as well.
## Custom Spell Effects
### Orb Effect
The spell orb effect is what will appear as a representation of your spell on the wheel. It uses a particle module for decoration, mesh modules for the two outer rings and the rune and an audio module for when it is selected.
To put together a spell orb that's unique from the other vanilla spells, you will need to have a custom material for the meshes and a custom particle system. If you want to be fancy you can also design a custom mesh for the rune and even a custom select sound.
Once you have all the parts designed that you want to use for the orb you can take a base orb from the vanilla jsons. I will use SpellOrbFire for the guide. Next, you can replace all the addresses with your custom ones.
I will show you all the things you will change. It would be best if you use ctrl+f or something similar to find each area since I they are not in order.
#### SpellOrbFire.json
```Json=1
"meshAddress": "Bas.Mesh.SpellWheel[o_outer_1]",
"materials": [
{
"$type": "ThunderRoad.EffectModuleMesh+Materials+Material, ThunderRoad",
"materialAddress": "Bas.Material.Spell.Wheel.Fire.Circles.HDR"
}
],
"meshAddress": "Bas.Mesh.SpellWheel[o_outer_2]",
"materials": [
{
"$type": "ThunderRoad.EffectModuleMesh+Materials+Material, ThunderRoad",
"materialAddress": "Bas.Material.Spell.Wheel.Fire.Circles.HDR"
}
],
"meshAddress": "Bas.Mesh.SpellWheel[o_rune]",
"materials": [
{
"$type": "ThunderRoad.EffectModuleMesh+Materials+Material, ThunderRoad",
"materialAddress": "Bas.Material.Spell.Wheel.Fire.Runes.HDR"
}
],
"effectParticleAddress": "Bas.Particle.Spell.Fire.Orb.New",
"audioContainerAddress": "Bas.AudioGroup.Spell.Fire.Select",
```
### Charge Effect
I will be talking about the Charge and FingerCharge effect since they are very similar. For this section you only need two particle systems to make a unique-looking effect (One for fingers and one for the main effect). When making these particles make sure to have links to intensity in the `Effect Child Particles` component for the size, spawnrate and/or shape radius so that the charge effect will grow as you charge the spell.
You have the choice to make custom audio but I wouldn't suggest it if is your first time since you would need to create seamless looping, starting and stopping sfx which is quite hard. It's best if you just use one of the vanilla spell's charge sfx for now.
If you aren't making custom audio then make sure to choose the base charge effect jsons of whatever spell you want it to sound like. I will show fire to keep on theme and like before will show the things you can change.
#### SpellFireCharge.json
```Json=1
"audioContainerAddress": "Bas.AudioGroup.Spell.Fire.ChargeStart",
"audioContainerAddress": "Bas.AudioGroup.Spell.Fire.ChargeLoop2",
"audioContainerAddress": "Bas.AudioGroup.Spell.Fire.ChargeLoop",
"effectParticleAddress": "Bas.Particle.Spell.Fire.Charge",
"audioContainerAddress": "Bas.AudioGroup.Spell.Fire.ChargeStop",
"audioContainerAddress": "Bas.AudioGroup.Spell.Fire.ChargeLoop1",
```
#### SpellFireChargeFinger.json
```Json=1
"effectParticleAddress": "Bas.Particle.Spell.Fire.ChargeFinger",
```
Make sure to delete the vfx modules if you aren't using them.
### Imbue Effect
The imbue effect is made up out of audio, shader, particle and light effect modules.
The main parts you will want to change are the shader and particles. But if you've made custom audio for the charge you would probably want to mirror that here.
I've explained the shader effect previously so just experiment with the colour options until you find something you like.
In order to make the particle system follow the mesh you will need to set its shape to Mesh Renderer and then add a Quad 3D object to your prefab.
It should look something like this:

Once you have made the quad just drag it into Mesh from the shape options

#### ImbueFire.json
```Json=1
"audioContainerAddress": "Bas.AudioGroup.Spell.Fire.Imbue.Start",
"$type": "ThunderRoad.EffectModuleShader, ThunderRoad",
"linkBaseColor": "None",
"linkEmissionColor": "Main",
"linkExtraColor": "None",
"extraPropertyName": null,
"lifeTime": 5.0,
"refreshSpeed": 0.1,
"useSecondaryRenderer": true,
"mainColorStart": { -->
->"mainNoHdrColorEnd": {
"r": 1.0,
"g": 0.170902833,
"b": 0.0,
"a": 0.0
}
"effectParticleAddress": "Bas.Particle.Spell.Fire.ImbueBlade",
"audioContainerAddress": "Bas.AudioGroup.Spell.Fire.Imbue.Loop",
"audioContainerAddress": "Bas.AudioGroup.Whoosh.TorchLoop",
```
#### Other Effects
I can't go through all the effects mentioned before in as much detail as the 3 main ones but I will do a quick run through all the effects I haven't explained with the location, trigger and modules listed.
* `"readyEffectId"` - This is triggered at the location of the spell caster once it reaches the minimum charge amount. Audio and Particle
* `"throwEffectId"` - This is triggered at the location of the spell caster when a spell is thrown. Audio and Particle
* `"staffSlamTipEffectId"` - This is triggered at tip of a staff when it is slammed. Particle
* `"staffSlamCollisionEffectId"` - This is trigged at the point of collision when a staff is slammed. Particle