Skip to content

Adding a Character

Alchyr edited this page Apr 14, 2024 · 66 revisions

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.

image

It should look like this once you've added the files.

image

The Character Class

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.

image

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.

image

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.

image

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.

image

Again, 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).

Contents

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 getName() { return CardCrawlGame.languagePack.getCharacterString(ID).NAMES[0]; }
    private final CharacterStrings characterStrings = CardCrawlGame.languagePack.getCharacterString(ID);
    private final String[] NAMES = characterStrings.NAMES;
    private final String[] TEXT = characterStrings.TEXT;

This is the text information required for the character, which is loaded from localization files. The first two lines perform that loading process.

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.

    //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");

    //In-game images
    private static final String SHOULDER_1 = characterPath("shoulder.png"); //Shoulder 1 and 2 are used at rest sites.
    private static final String SHOULDER_2 = characterPath("shoulder2.png");
    private static final String CORPSE = characterPath("corpse.png"); //Corpse is when you die.

    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/.

Enums

    public static class Enums {
        //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 AbstractPlayer.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.

    //Methods that will be used in the main mod file
    public static void registerColor() {
        BaseMod.addColor(Enums.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.

Constructor

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.

Methods

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.

Card "Color"

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.

image

You'll be calling the registerColor method, which is pre-defined in the character class for convenience.

image

Adding the Character

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.

Subscriber

Right up at the top of the class, after implements, add EditCharactersSubscriber. Don't forget the comma afterwards!

image

That'll leave you with the following error:

image

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.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).

What Next

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.

The Things Not Required For Functionality

Visuals

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(NAMES[0], Enums.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.

Orb

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.

A set of example orb files.

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.

Example

Note that if you aren't using CustomOrb, you must override this method in your character class.

https://github.com/daviscook477/BaseMod/blob/1a8a344ecfde07613d29f39ff52b380cf50196f9/mod/src/main/java/basemod/abstracts/CustomPlayer.java#L156

Cards

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).

Colors

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.

Energy Font

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

Heart Kill Cutscene

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.

Clone this wiki locally