-
Notifications
You must be signed in to change notification settings - Fork 22
Adding a Character
A character in Slay the Spire has a lot of separate parts which are all required for it to work.
To make things easier, you should download this set of example assets. A character needs a large number of images, and it's easier to add them all at once so you can adjust them to your liking later rather than to have you make them step-by-step during the tutorial.
The main goal of this tutorial is just for you to have a rough understanding of everything that's necessary, so code and resources are provided and explained, rather than stating what you'll need to make.
After downloading them, extract the files to resources/yourmod/images/character
. Be sure you're putting the files into resources
and not java
.
It should look like this once you've added the files.
The first step for making your character is to make the file for your character's class. I recommend creating a new character
package in your mod's package for it first. Make sure you're doing this in the java
folder, not still in the resources
folder.
The character class requires many methods to be filled out, most of which are the same every time and so like the images, this template file is provided for you to use. Download it and copy it into the package you created. If it doesn't show up immediately, you may need to right-click->Reload from Disk
to make IntelliJ update the package's contents.
It should have a few errors; these are intentional. First, is the package. If you don't see this error, that just means IntelliJ fixed it already.
If you have this error, press Alt+Enter
and select Set package name
to fix it. The package should end up as the first line of the file, matching the location of it in the folder structure.
Next are some unresolved methods. These are defined in the main mod file, and weren't set up to be imported ahead of time as the specifics would change depending on what you've named your mod.
Use Alt+Enter
and make sure you choose Import static method
or Qualify static call
. Either one will work fine. This should include the characterPath
and makeID
methods.
With this done, the character class itself should be error-free. I suggest renaming the class later, to make this setup easier (so the class name matches what's shown in the tutorial).
This is a quick review of the contents of the character class file.
//Stats
public static final int ENERGY_PER_TURN = 3;
public static final int MAX_HP = 70;
public static final int STARTING_GOLD = 99;
public static final int CARD_DRAW = 5;
public static final int ORB_SLOTS = 0;
For the majority of characters, max HP is the only value that is adjusted. These values are defined here for convenience and used later in the constructor of the character class.
//Strings
private static final String ID = makeID("CharacterID"); //This should match whatever you have in the CharacterStrings.json file
private static String[] getNames() { return CardCrawlGame.languagePack.getCharacterString(ID).NAMES; }
private static String[] getText() { return CardCrawlGame.languagePack.getCharacterString(ID).TEXT; }
This is the text information required for the character, which is loaded from localization files.
If you look at the resources/localization/eng/CharacterStrings.json
file, you should see
"${modID}:CharacterID": {
This is the identifier for the text that follows. Adjust "CharacterID" in both places to be whatever you'd like your character to be called. If you were making the Ironclad, you would have
private static final String ID = makeID("Ironclad");
in the character class and "${modID}:Ironclad": {
in the json.
You should also adjust the rest of the contents of CharacterStrings.json
.
Next, the Meta
static class. As the comment states, it's necessary for reasons that are too complicated and not relevant for the scope of this tutorial.
//This static class is necessary to avoid certain quirks of Java classloading when registering the character.
public static class Meta {
//These are used to identify your character, as well as your character's card color.
//Library color is basically the same as card color, but you need both because that's how the game was made.
@SpireEnum
public static PlayerClass YOUR_CHARACTER;
@SpireEnum(name = "CHARACTER_GRAY_COLOR") // These two MUST match. Change it to something unique for your character.
public static AbstractCard.CardColor CARD_COLOR;
@SpireEnum(name = "CHARACTER_GRAY_COLOR") @SuppressWarnings("unused")
public static CardLibrary.LibraryType LIBRARY_COLOR;
These are enum values, representing your character's class and card color. Slay the Spire uses an enum to hold these, which normally cannot be modified. @SpireEnum
is a feature of ModTheSpire which adds onto these enums. Enum values are generally named using all capital letters. Note that these are just names; changing card appearance requires modifying their image files.
Change YOUR_CHARACTER
and "CHARACTER_GRAY_COLOR"
to unique identifications fitting for your character. As the comment mentions, you must change "CHARACTER_GRAY_COLOR"
in both places, and they must be identical. CARD_COLOR
and LIBRARY_COLOR
don't need to be changed.
//Character select images
private static final String CHAR_SELECT_BUTTON = characterPath("select/button.png");
private static final String CHAR_SELECT_PORTRAIT = characterPath("select/portrait.png");
//Character card images
private static final String BG_ATTACK = characterPath("cardback/bg_attack.png");
private static final String BG_ATTACK_P = characterPath("cardback/bg_attack_p.png");
private static final String BG_SKILL = characterPath("cardback/bg_skill.png");
private static final String BG_SKILL_P = characterPath("cardback/bg_skill_p.png");
private static final String BG_POWER = characterPath("cardback/bg_power.png");
etc, more images
These are the file paths to the images used by the character. If you put the example assets in the correct folder, these shouldn't require any adjustments. You can ctrl+click
the characterPath
method to see how it creates the file path. Changing the appearance of your character's cards and many other bits will require changing these images, which should exist under resources
in the left sidebar at resources/yourmod/images/character/
.
//Methods that will be used in the main mod file
public static void registerColor() {
BaseMod.addColor(CARD_COLOR, cardColor,
BG_ATTACK, BG_SKILL, BG_POWER, ENERGY_ORB,
BG_ATTACK_P, BG_SKILL_P, BG_POWER_P, ENERGY_ORB_P,
SMALL_ORB);
}
public static void registerCharacter() {
BaseMod.addCharacter(new MyCharacter(), CHAR_SELECT_BUTTON, CHAR_SELECT_PORTRAIT);
}
These methods will be used in the main mod file to perform the actual registration of the card color and character class with BaseMod. They don't need to be modified.
For now, we'll skip over the constructor. It'll work without changing anything, so we'll come back to it when it's time to explain how to change how your character looks.
The methods are ordered based on how likely you are to need to change them, with the things you'll have to change at the top and the things you shouldn't change at the bottom.
The two really important ones are getStartingDeck()
and getStartingRelic()
. As their names suggest, these are used to define your character's starting deck and starting relic. For now, they're using some existing content as a placeholder. Once you make some cards and a starting relic, you'll have to adjust these. The rest are less important, and well commented. Feel free to read them, and adjust some of them if you want.
Next you'll need to register the character's cards' color. This isn't an actual color, just a categorization to define how they look. Each card color is defined by a set of images used to render the card. Every card in Slay the Spire is connected to a "color", such as Colorless, Curse, and the colors for each individual character. This registration is done in the main mod file.
Go to your main mod file and find the initialize()
method. This is where you'll actually be adding the color.
You'll be calling the registerColor
method, which is pre-defined in the character class for convenience.
public static void initialize() {
new MyMod();
MyCharacter.Meta.registerColor();
}
The last step in making your character functional is registering the character class. For this, you'll have to add a subscriber to your main mod file.
Right up at the top of the class, after implements
, add EditCharactersSubscriber
. Don't forget the comma afterwards!
That'll leave you with the following error:
If you don't see it, click where the red lines are and hit Alt+Enter
. Choose Implement methods
and click Ok
on the following window.
This will make an empty method.
@Override
public void receiveEditCharacters() {
}
If you don't like where it is, you can cut and paste it somewhere else (in the same file). In this method, you'll be registering your character using the registerCharacter
method defined in the player class.
@Override
public void receiveEditCharacters() {
MyCharacter.Meta.registerCharacter();
}
Your character should now be selectable in-game. Test it and make sure everything is working. You'll crash at the end of combat, however, as your character doesn't have enough cards yet.
If it does, congratulations! You've gotten through one of the harder steps of the process. Now that it's setup properly, feel free to right click your character class and choose Refactor->Rename to call it whatever you'd like.
Note that you will crash at the end of combats for now (more details below).
From here, all that's left is adding more content like cards, relics, and maybe some potions. And of course, all the details like the visuals (see below for more details on that). If you're feeling extra creative, maybe some events? It's up to you.
Standard characters have a pool of 75 cards, in a ratio of approximately 1:2:1 Common:Uncommon:Rare.
You'll need at least 3 common, uncommon, and rare cards to avoid card rewards freezing/crashing the game, and two attacks, two skills, and one power for shops to not crash. If you pick up question card you'd need 4 of each rarity. Until you fill out the card pool, expect crashes during testing. You can use Prismatic Shard as the starting relic if you want to avoid dealing with this right away.
They have 1 starter relic, and some number of relics spread out in the common, uncommon, and rare pools. All basegame characters have 1 common relic. Watcher has 2 uncommon relics, the others have 1. Ironclad and Silent have 3 rare relics; Watcher has 2, and Defect has 1. Characters generally have 3 boss relics. 1 starter relic upgrade (please make yours better than the basegame upgrades), 1 non-energy relic, and 1 energy relic. Watcher only has 2 boss relics.
Focus more on cards than relics; character specific relics (besides the starter) are relatively uncommon to encounter and don't harm the experience too much if they're missing. But they do add a bit of nice variety to the gameplay since they can interact directly with character mechanics, unlike generic relics.
Now that you have things working, you can worry a bit about making things look good.
In the constructor of your character class, there's a line labeled //orb
and a line labeled //animation
.
These are the big energy orb in the bottom left and the animation of your character respectively.
For the animation, you can view this page for details on what kind of animations you can use.
To just use a static image:
super(getNames()[0], Meta.YOUR_CHARACTER,
new CustomEnergyOrb(null, null, null), //Energy Orb
new AbstractAnimation() { //Change the animation line to this.
@Override
public Type type() {
return Type.NONE; //A NONE animation results in img being used instead.
}
});
initializeClass(characterPath("image.png"),
SHOULDER_2,
SHOULDER_1,
CORPSE,
This example loads resources/yourmod/images/character/image.png
as the character image.
CustomOrb
is the default energy orb for custom players. You can give it an array of images along with numbers representing their rotation speed, and it'll render those images on top of each other spinning at those speeds. orbVfxPath
is the file for an image that will expand out and fade when the player gains energy.
These images should be 128x128, except for the vfx image which should be 256x256.
An example of using CustomOrb.
You don't have to use CustomOrb, though. You can make your own class that implements EnergyOrbInterface
and do anything you want with it. No detailed instructions here, since what you do at that point is up to your own creativity.
Note that if you aren't using CustomOrb, you must override this method in your character class.
The character's cards are displayed using the images in the cardback
folder. To change how they look, you'll have to edit these images. energy_orb.png
is the orb on the top left of the card where cost is displayed. energy_orb_p.png
is the same, but when viewing a single card in the larger view. small_orb.png
is used in card text to represent energy. The various bg_
images are the backgrounds for the various card types, with the _p
versions for portrait (single card) view. Note that all cards other than attacks and powers use the skill background (curses, statuses).
Of the colors defined in the character class, cardRenderColor
, cardTrailColor
, and slashAttackColor
, cardTrailColor
is the most visible one. But you should probably change all three regardless.
If you want an example of loading and using a custom energy font, you can look here.
https://github.com/Alchyr/sumireko/blob/master/src/main/java/sumireko/patches/EnergyFontGen.java
https://github.com/Alchyr/sumireko/blob/master/src/main/java/sumireko/character/Sumireko.java#L220
To change this from the default Ironclad cutscene, override the getCutscenePanels
method in your player class.
An example. The glow_fade
can be ignored, as that is specific to this example.
It should return a list of CutscenePanel
, each of which has one image and an optional sound effect to play when that image appears. The images should be 1920x1200, placed to fit within the cutscene border. Example images
If you've read all the comments and still have questions, go to the #modding-technical channel in the Slay the Spire discord.