Skip to content

Getting Started (For Modders)

Alex Driedger edited this page Jun 9, 2018 · 22 revisions

Getting Started With Modding Slay The Spire

Note that this guide is for Windows users. If you're using OS X or Linux most of the tutorial will be applicable but specific file locations, etc... may be different.

Slay The Spire is written in Java. So you'll need some prior experience using the Java Programming Language or be willing to learn. This tutorial will use three tools you'll need to download:

  1. The Eclipse IDE: link
  2. The JD-GUI Java Decompiler: link - click the download tab and then grab the .jar version which will look something like jd-gui-1.4.0.jar We will be using Eclipse as our development environment for creating the mod and we will use JD-GUI as a way to read a decompiled version of the game's source code.
  3. Maven: link

Decompiling your game

Decompiling the game will allow you to (essentially) look at the game's source code. This isn't necessary for modding if you only use BaseMod's subscription system. However, it is a very useful tool for

  • Figuring out how different classes in the game work
  • Figuring out where BaseMod's hooks are and when they are called
  • Doing any modding beyond what BaseMod currently provides

Follow these steps to decompile the game:

  1. Launch JD-GUI with jd-gui.exe and then when it prompts you to find a jar to decompile go ahead and navigate to your Slay The Spire folder and select desktop-1.0.jar. If it does not automatically prompt you go to File -> Open File... Open File Image
  2. This will open a view in which you can look at the decompiled source code.
  3. Click File -> Save All Sources and save everything as desktop-1.0.jar.src.zip wherever you are going to be doing your modding. The output will be a zip file which you will then have to unzip to access all of the source code (note that it is not actual source code - just reconstructed source code by the decompiler). Save All Sources Image

Modding tools

In order to do modding for Slay The Spire you're absolutely going to need ModTheSpire link. It's also recommended that you download BaseMod to have access to an easier modding API link.

Folder setup

For this tutorial we are going to be using a folder called my_mods in your Documents folder. This folder (the my_mods folder will be where you can place all your mods). For this example mod we will create an example_mod folder inside my_mods to contain all the code for this mod. Directory Structure

Now inside the my_mods folder let's go ahead and create another folder called lib (lib is short for libraries). This folder is where we'll place the dependencies for your mod. Go ahead and move the ModTheSpire.jar and BaseMod.jar that you downloaded in the last step into your lib folder. Lib Folder

Setting up Eclipse

If this is your first time using Eclipse it will prompt you to select a Workspace location when you open it up. For the Workspace DO NOT use the default workspace. Instead we're going to use your my_mods folder as the workspace. If you did end up using the default workspace or have used Eclipse before we're going to switch workspaces over to my_mods. The black bars in the image are just covering up some personal information. Switch Workspace New Workspace

Now we can go ahead an create our mod using what Eclipse calls a project. So we will go to File -> New -> Java Project. New Java Project This will popup a modal. In the spot for the name of the project enter example_mod and then click Finish. New Java Project Modal. This will create a new project for your mod! However we're not done yet because we have to configure its dependencies. Remember how earlier we put ModTheSpire.jar and BaseMod.jar in the lib folder? Well now we have to tell Eclipse that we actually put the dependencies there since it won't automatically know.

Dependencies

Your mod actually has one more dependency that we didn't download earlier (because if we could have downloaded it earlier it would be piracy). This is because we now need desktop-1.0.jar as a dependency. So copy it over from your Slay The Spire directory into lib so that your lib folder now looks like this. Final Lib Folder.

To tell Eclipse about these dependencies we're going to need to go into the settings for your example_mod. To do this right click on example_mod and go down to Properties. Properties.

From here we're going to go to Java Build Path -> Libraries. Build Path.

Now use Add External JARs... to add ModTheSpire.jar, BaseMod.jar, and desktop-1.0.jar. Click okay once you've added all of them. It should look like this: All Jars.

Maven

Slay The Spire mods for the most part use Maven for building. If you have not installed Maven, go ahead and do that first. Maven requires your mod's folder structure to be set up in a specific way. Specifically all of your source code will need to be in the src/main/java folder. To make this happen go back to the settings for your mod. Properties Again.

From here we're going to go to Java Build Path -> Source and then highlight the example_mod/src folder and click Remove to remove it. Then use Add Folder to add src/main/java as a source folder. Click okay once you've done this. It should like like: New Source Folder

You will need to have a file called pom.xml in your top level directory. This is required for Maven to build your project. An example pom.xml file can be found in this gist.

Make sure your dependencies are pointing to the location of the correct .jar files. ${basedir} is the directory where your pom.xml file is located. .. is the parent directory.

<dependency>
    <groupId>basemod</groupId>
    <artifactId>basemod</artifactId>
    <version>2.10.0</version>
    <scope>system</scope>
    <systemPath>${basedir}/../lib/BaseMod.jar</systemPath> // systemPath should be the path to where BaseMod.jar is located
</dependency>

Use mvn package to build your mod! Maven will build a .jar file with your mod. Your compiled .jar file location is specified in the pom.xml file

<target>
    <copy file="target/ExampleMod.jar" tofile="../lib/ExampleMod.jar"/> // tofile location is where you can find your compiled .jar file
</target>

Actually Modding!

Congratulations! If you've made it this far you've finished setting up your development environment and should be ready to actually start developing your mod. Before continuing we're going to go over some of the fundamentals of how ModTheSpire and BaseMod work so you can use them to make your mod. ModTheSpire is the modloader for Slay The Spire and allows you to directly make changes to the game's code. This process is a bit tedious and subject to changes every time Slay The Spire is patched but may be necessary for some particularly complex features that mods try to add. BaseMod provides a more convenient API so mods for the most part can avoid directly changing the game's code. As such you are likely to use BaseMod and ModTheSpire in combination for modding projects. BaseMod's API is focused around the idea of firing events and having mods subscribe to/listen for those events. For example if you want to make a mod that heals the player 5 HP every time a card is exhausted you would use BaseMod to subscribe to the PostExhaust event and then you could would be able to listen for that event using an event handler. In this example we will be creating a simple mod that shows how to use BaseMod's subscription system. In the future there may be tutorials showing how to use the Custom Character system and GUI but for now if you wish to use those systems take a look at the documentation here on the wiki and other mods that already use them.

@SpireInitializer

To make a mod that works with BaseMod you're going to want to start out with some boilerplate code.

package example_mod;

import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer;

@SpireInitializer
public class ExampleMod {

    public ExampleMod() {
        // TODO: make an awesome mod!
    }

    public static void initialize() {
        new ExampleMod();
    }

}

Note: If Eclipse can't find @SpireInitializer you may have to go through the Dependencies step again since Eclipse seems to reset it when you changes source folders.

So what the above code is doing is telling ModTheSpire that we have a class called ExampleMod that has a method called initialize that we would like to have called once mods are loaded but before the game starts up. Inside that initialize method we create an instance of our mod and inside that instance is where we're going to be telling BaseMod about the different events we want to listen for.

ISubscriber

For this example we're going to be counting the number of times a card is exhausted in each battle and then report that value to the log. We will also be tracking the total number of cards exhausted in each act and reporting that after each battle too. Basically this mod could figure out that the player exhausted 2 cards in the 1st battle, then 3 cards in the 2nd battle which is 5 cards overall, etc.. To do this we will need to use three events that BaseMod provides: PostExhaust, PostBattle, PostDungeonInitialize

We will be using those hooks as follows. In PostDungeonInitialize we will set the number of cards exhausted this act to 0. In PostBattle we will print the total number of cards exhausted and the number of cards exhausted this battle. In PostExhaust we will count the number of cards exhausted.

To setup our mod to subscribe to these events we need our ExampleMod class to implement the corresponding interfaces: PostExhaustSubscriber, PostBattleSubscriber, PostDungeonInitializeSubscriber. Then we tell BaseMod that we want to listen for those events by using BaseMod.subscribe(this).

package example_mod;

import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.rooms.AbstractRoom;

import basemod.BaseMod;
import basemod.interfaces.PostBattleSubscriber;
import basemod.interfaces.PostDungeonInitializeSubscriber;
import basemod.interfaces.PostExhaustSubscriber;

@SpireInitializer
public class ExampleMod implements PostExhaustSubscriber,
	PostBattleSubscriber, PostDungeonInitializeSubscriber {

	public ExampleMod() {
		BaseMod.subscribe(this);
	}
	
	public static void initialize() {
		new ExampleMod();
	}
	
	@Override
	public void receivePostExhaust(AbstractCard c) {

	}
	
	@Override
	public void receivePostBattle(AbstractRoom r) {
		
	}
	
	@Override
	public void receivePostDungeonInitialize() {
		
	}
	
}

Mod Logic

package example_mod;

import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.rooms.AbstractRoom;

import basemod.BaseMod;
import basemod.interfaces.PostBattleSubscriber;
import basemod.interfaces.PostDungeonInitializeSubscriber;
import basemod.interfaces.PostExhaustSubscriber;

@SpireInitializer
public class ExampleMod implements PostExhaustSubscriber,
	PostBattleSubscriber, PostDungeonInitializeSubscriber {

	private int count, totalCount;
	
	private void resetCounts() {
		totalCount = count = 0;
	}
	
	public ExampleMod() {
		BaseMod.subscribe(this);
		resetCounts();
	}
	
	public static void initialize() {
		new ExampleMod();
	}
	
	@Override
	public void receivePostExhaust(AbstractCard c) {
		count++;
		totalCount++;
	}
	
	@Override
	public void receivePostBattle(AbstractRoom r) {
		System.out.println(count + " cards were exhausted this battle, " +
			totalCount + " cards have been exhausted so far this act.");
		count = 0;
	}
	
	@Override
	public void receivePostDungeonInitialize() {
		resetCounts();
	}
	
}
Clone this wiki locally