Skip to content

Adding Relics

Alchyr edited this page Apr 20, 2022 · 27 revisions

Before working on relics, I would recommend making sure you understand the action queue.

If you've already read the tutorial on cards, this will likely seem very similar.


Registering Your Relics

The process of making relics has two steps.

Step 1: Make the relic.

Step 2: Register the relic.

AutoAdd is a feature of BaseMod that allows you to avoid manually registering every relic you make.

Setting up AutoAdd for relics

First, in your main mod file, add EditRelicsSubscriber to the subscribers at the top, and then implement the receiveEditRelics method.

public class MyMod implements
        EditRelicsSubscriber, //up at the top
        EditStringsSubscriber,
    @Override
    public void receiveEditRelics() { //somewhere in the class
        
    }

AutoAdd for relics is a bit more complicated than AutoAdd for cards. All cards are normally registered with the same method, but relics are registered differently depending on their type. Specifically, character-specific relics have to be registered differently from general relics. This code utilizes information stored in the BaseRelic class to register the relic appropriately.

        new AutoAdd(modID) //Loads files from this mod
            .packageFilter(BaseRelic.class) //In the same package as this class
            .any(BaseRelic.class, (info, relic) -> { //Run this code for any classes that extend this class
                if (relic.pool != null)
                    BaseMod.addRelicToCustomPool(relic, relic.pool); //Register a custom character specific relic
                else
                    BaseMod.addRelic(relic, relic.relicType); //Register a shared or base game character specific relic

                if (info.seen) { //If the class is annotated with @AutoAdd.Seen, it will be marked as seen, making it visible in the relic library.
                    UnlockTracker.markRelicAsSeen(relic.relicId);
                }
            });

You may need to use Alt+Enter to import some classes. Any relics you make should now be added automatically, as long as they're in the same package as BaseRelic. This includes sub-packages. If you want the details of how AutoAdd works, check the BaseMod wiki page for documentation. This code is made to work with how BasicMod is setup and will require changes if you are not using BasicMod.

If you make relics that don't extend the BaseRelic class, they will not be registered by this AutoAdd. You can register them manually with BaseMod.addRelic/addRelicToCustomPool(new YourRelic(), pool) or set up AutoAdd differently.

Making Relics

You should make your first relic from scratch to understand all the parts, but after that feel free to copy and paste it and just change what you need.

The first step is to make a new class in the relics package.

image

You could also organize your relics using packages, such as having a package for common, uncommon, and rare relics.

All relics in the game extend from AbstractRelic, the class used as the base for every relic. The BaseRelic class is an abstract class with some small features to make it easier to create relics. The main point is that it stores information that the previously set up AutoAdd can use to register the relic with BaseMod correctly.

public class MyRelic extends BaseRelic {

}

Relic Properties

First, you'll be defining a few properties that all relics need.

public class MyRelic extends BaseRelic {
    private static final String NAME = "MyRelic"; //The name will be used for determining the image file as well as the ID.
    public static final String ID = makeID(NAME); //This adds the mod's prefix to the relic ID, resulting in modID:MyRelic
    private static final RelicTier RARITY = RelicTier.COMMON; //The relic's rarity.
    private static final LandingSound SOUND = LandingSound.CLINK; //The sound played when the relic is clicked.
}

Every relic has to have a unique ID. By adding a prefix based on the mod id to your relic's name, it greatly reduces the likelihood of issues with other mods having relics with the same ID. Since the ID is used in various places, such as to define the player's starting relic, having the ID as a public constant is useful.

I recommend always having the name and ID defined as constants, but for these next two it's fine to just use them directly in the constructor. They're defined here to make it easier to explain what they're for.

RARITY is the relic's rarity.

RelicTier.STARTER is for starting relics. COMMON, UNCOMMON, and RARE are self-explanatory. SHOP relics can only be obtained from the third relic slot in a shop, which is always a SHOP relic. BOSS is for boss relics. SPECIAL is for relics that are only obtained from events/cannot be normally obtained.

SOUND is the sound played when the relic is picked up/clicked. The names somewhat describe the sound, so just use whichever is most fitting for your relic.

Constructor

BaseRelic has two different constructors. One is for general relics, and the other is for character specific relics.

public class MyRelic extends BaseRelic {
    private static final String NAME = "MyRelic";
    public static final String ID = makeID(NAME);

    public MyRelic() {
        super(ID, NAME, MyCharacter.Enums.CARD_COLOR, RelicTier.COMMON, LandingSound.CLINK);
    }
}

This example is using the constructor for a character-specific relic. The third parameter is the card color of the character the relic is for. For an Ironclad only relic, it would be AbstractCard.CardColor.RED. If you want to make a general relic, just omit that parameter. In this case, it would look like:

super(ID, NAME, RelicTier.COMMON, LandingSound.CLINK);

BaseRelic will use NAME to determine the relic's image files. We'll go over setting up the relic's images at the end.

How Relics Work

Right now, you have a relic that does nothing. To make it do things, you need to override methods defined in AbstractRelic known as hooks. These methods are all linked to specific events during gameplay, such as the start of combat, taking damage, or entering a new room.

The easiest way to know what method to override is to look at a base game example that triggers at the time you want. If you wanted a relic that does something at the end of combat, you could look at Ironclad's Burning Blood to find that it overrides onVictory. If you can't think of an example, you can see all of the methods by viewing the AbstractCard class with a decompiler (mentioned near the start of this).

Special Cases

Occasionally, you might want to make a relic that doesn't have any corresponding hook method for you to override. The original game also has relics like these, where it was something specific enough that making a hook was inconvenient/unnecessary. For example, Unceasing Top has no code in the relic itself. All the code takes place in various places where cards are removed from the player's hand.

For relics like these, you can first see if StSLib or BaseMod's subscribers provide the hook you need. If they don't, you'll need to do some patching to change the game's code to do what you want.

Adding Some Functionality

For this example relic, we'll make a relic that gives the player 10 Strength whenever they play a card.

To figure out what hook to use, you could look at a relic like Ink Bottle which counts cards played. Many other relics use the same hook, such as Pen Nib and Mummified Hand.

public class MyRelic extends BaseRelic {
    private static final String NAME = "MyRelic";
    public static final String ID = makeID(NAME);

    private static final int STRENGTH = 10; //For convenience of changing it later and clearly knowing what the number means instead of just having a 10 sitting around in the code.

    public MyRelic() {
        super(ID, NAME, MyCharacter.Enums.CARD_COLOR, RelicTier.COMMON, LandingSound.CLINK);
    }


    @Override
    public void onUseCard(AbstractCard targetCard, UseCardAction useCardAction) {
        super.onUseCard(targetCard, useCardAction);
    }
    // Take advantage of autocomplete!
    // If you type "public onUse" IntelliJ should already have the method in the suggestions. 
    // Use the up/down arrows to select it and press enter to automatically create this whole chunk.
    // This autocomplete is also a good way to see all the hooks/look for the right hook by name, by just typing "publi"
}

wip

Clone this wiki locally