Skip to content

Spell01 Basic Damaging Spell

Brandon edited this page Oct 6, 2026 · 7 revisions

Part 1: Basic Damaging Spell (Not Horn Break)

← Custom Spell Cards Overview | Next: Part 2: Status Effects & Rooms →

In this first tutorial, we will build the most fundamental type of spell: a direct-damage card that targets an individual unit. We'll base this on the Hellhorned clan's Horn Break by creating Not Horn Break, a 1-Ember card that deals 5 Piercing damage.

Along the way, you'll learn the structure of a Trainworks Reloaded card definition and how to wire up custom artwork.


1. Asset Setup

Download the custom card art:

  • Download Art: NotHornBreak.png
  • Save Location: Place the file in your project under textures/NotHornBreak.png.

Important

Keep card artwork dimensions at or below 468x468. While larger images will scale down to fit the card frame, large image files noticeably degrade startup times because file I/O is the primary loading bottleneck.


2. Complete JSON Definition

In Plugin.cs add the path "json/spells/NotHornBreak.json" to the AddMergedJsonFile call. Then, create a new JSON file NotHornBreak.json in the folder json/spells in your mod project. In NotHornBreak.json add the following content:

{
  "$schema": "https://github.com/Monster-Train-2-Modding-Group/Trainworks-Reloaded/releases/latest/download/schema.json",
  "cards": [
    {
      "id": "NotHornBreak",
      "cost": 1,
      "names": {
        "english": "Not Horn Break"
      },
      "descriptions": {
        "english": "Deal [effect0.power] damage."
      },
      "rarity": "common",
      "targets_room": true,
      "targetless": false,
      "class": "ClassBanished",
      "card_art": "@NotHornBreakCardArt",
      "pools": [
        "MegaPool"
      ],
      "traits": [
        "@PiercingTrait"
      ],
      "effects": [
        "@Deal5Damage"
      ]
    }
  ],
  "traits": [
    {
      "id": "PiercingTrait",
      "name": "CardTraitIgnoreArmor"
    }
  ],
  "effects": [
    {
      "id": "Deal5Damage",
      "name": "CardEffectDamage",
      "target_mode": "drop_target_character",
      "target_team": "both",
      "param_int": 5
    }
  ],
  "game_objects": [
    {
      "id": "NotHornBreakCardArt",
      "type": "card_art",
      "extensions": {
        "card_art": {
          "sprite": "@NotHornBreakSprite"
        }
      }
    }
  ],
  "sprites": [
    {
      "id": "NotHornBreakSprite",
      "path": "textures/NotHornBreak.png"
    }
  ]
}

3. Breaking Down the Recipe

The Schema Header

"$schema": "https://github.com/Monster-Train-2-Modding-Group/Trainworks-Reloaded/releases/latest/download/schema.json"

Always include this at the top of your files. It gives your editor schema validation, syntax checking, autocomplete, and tooltips explaining properties from the editor when moused over.

The link will direct you towards the official JSON schema which is how you interface with the modding framework to create or modify content. The schema file is quite large, so if you are looking for the definitions of a specific item an easier to view version of the schema is in the Trainworks reloaded repo.

For instance here are links to the full schema for each item in this tutorial


Card Basics (cards)

  • id: A unique string identifying this card. All objects (cards, traits, effects, art) require unique IDs.
  • names: Localized names. English is set here, but the game natively supports french, german, russian, portuguese, chinese, spanish, chinese_traditional, korean, and japanese.
  • descriptions: The card text. Never hardcode any numbers in the description. Use [effect0.power] so that dynamic scaling, upgrades, and spell damage boosts reflect correctly in the text.
  • cost: Ember cost to play the card.
  • rarity: Cards can be common, uncommon, rare, or champion rarities.
  • targets_room / targetless: Defines how the player targets with this card. These don’t specify what is targeted, only how targeting behaves. Settings targetless to false gives the arrow the player can use to select a unit.
  • class: The clan this card belongs to (e.g., ClassBanished, ClassPyreborne, or your custom clan).
  • traits, effects, pools, card_art: These properties contain references to other objects we define below. Always prefix references to objects you create in your own JSON files with @. In the above class property since we do not define the Banished clan, we refer to the class by its name ClassBanished we also do not prefix the name with '@`. Remember this.

Important

  • References (@): Whenever you refer to an object defined inside your own JSON files (like @PiercingTrait, @Deal5Damage, or @NotHornBreakCardArt), prefix the ID with @. When defining an id like in the above card definition we do not prefix that with '@' as we are defining a new item.

Card Traits (traits)

"traits": [
  {
    "id": "PiercingTrait",
    "name": "CardTraitIgnoreArmor"
  }
]

Here we define the trait we will attach to the card. Card Traits attach keywords or special modifiers to cards. CardTraitIgnoreArmor is the games internal name for the Piercing keyword.

Properties

  • id: The internal ID for this card trait. No @ prefix is needed since we’re defining it here.
  • name: This required property specifies the class name that defines how this CardTrait behaves. As stated above, the Piercing keyword is handled by the class CardTraitIgnoreArmor. This class is code you can look up in the C# decompiler to see its definition.

Card Effects (effects)

"effects": [
  {
    "id": "Deal5Damage",
    "name": "CardEffectDamage",
    "target_mode": "drop_target_character",
    "target_team": "both",
    "param_int": 5
  }
]

Properties

  • name: The effect execution class. CardEffectDamage inflicts damage. This is also a class you can look up in the C# decompiler.
  • target_mode: "drop_target_character" targets whatever character the player drops the targeting arrow onto.
  • target_team: Set to "both", allowing the card to target either enemies (heroes) or friendly units (monsters).
  • param_int: The numerical argument passed to the effect—here, the base damage amount (5). This maps to [effect0.power] in the description.

Artwork Pipeline (game_objects & sprites)

"game_objects": [
  {
    "id": "NotHornBreakCardArt",
    "type": "card_art",
    "extensions": {
      "card_art": {
        "sprite": "@NotHornBreakSprite"
      }
    }
  }
],
"sprites": [
  {
    "id": "NotHornBreakSprite",
    "path": "textures/NotHornBreak.png"
  }
]

Trainworks pairs an abstract card art object with a raw texture file:

  1. sprites: Points directly to the file path on disk (textures/NotHornBreak.png).
  2. game_objects: Defines a GameObject card_art container that assigns the sprite to the card frame.

4. Testing Your Card

  1. Build your mod solution and ensure your output and textures/ folders are copied to BepInEx/plugins/<YourMod>/.
  2. Launch the game and inspect the BepInEx console log:
    • Verify there are no red/yellow error messages regarding invalid IDs or missing sprites.
  3. Start a run or open the Logbook/test deck:
    • You can use the Cheat console/GUI mods to give your card or mark it discovered. (use the backtick character to open the console or F2 to open the GUI)
    • Confirm the card costs 1 Ember, shows the Banished card border, displays your custom art, and reads: "Deal 5 damage.".
    • Play the card against an armored enemy unit to confirm the Piercing trait bypasses Armor and damages Health directly.


Next Steps

Now that you have basic targeted damage working, continue to Part 2: Status Effects & Rooms (Give Everyone Armor) to learn how to grant buffs/debuffs and hit an entire floor at once.

Clone this wiki locally