-
Notifications
You must be signed in to change notification settings - Fork 22
Adding Potions
The process of making potions has two steps.
Step 1: Make the potion.
Step 2: Register the potion.
AutoAdd is a feature of BaseMod that allows you to avoid manually registering every potion you make.
Potions do not have any specific subscriber you need to add to your main mod file to register them in. Instead, they're simply registered in receivePostInitialize
, which you should already have in the main mod file.
For organization, we'll make a separate static method to register all the potions in, and just call that method in receivePostInitialize
.
@Override
public void receivePostInitialize() {
registerPotions();
//... the rest of the method
}
public static void registerPotions() {
}
You can find a bit more explanation of AutoAdd in the pages for adding cards and relics. This code utilizes information stored in the BasePotion class to register the potion appropriately.
public static void registerPotions() {
new AutoAdd(modID) //Loads files from this mod
.packageFilter(BasePotion.class) //In the same package as this class
.any(BasePotion.class, (info, potion) -> { //Run this code for any classes that extend this class
//These three null parameters are colors.
//If they're not null, they'll overwrite whatever color is set in the potions themselves.
//This is an old feature added before having potions determine their own color was possible.
BaseMod.addPotion(potion.getClass(), null, null, null, potion.ID, potion.playerClass);
//playerClass will make a potion character-specific. By default, it's null and will do nothing.
});
}
You may need to use Alt+Enter
to import some classes. Any potions you make should now be added automatically, as long as they're in the same package as BasePotion
. 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 BasePotion and will not work if you are not using this abstract class.
If you make potions that don't extend the BasePotion
class, they will not be registered by this AutoAdd. You can register them manually with BaseMod.addPotion
or set up AutoAdd differently.
The BasePotion
class has a lot of code to simplify the potion creation process. If looking at other potions as examples, look primarily at their constructors and use
methods.
The first step is to make a new class in the potions
package.
You should probably organize your potions using packages, such as having a package for common, uncommon, and rare potions.
public class MyPotion extends BasePotion {
}
First, you'll need to define the potion's ID.
public class MyPotion extends BasePotion {
public static final String ID = makeID("MyPotion");
}
Every potion has to have a unique ID. You can also use something like makeID(MyPotion.class.getSimpleName())
. This will have the same result, but if you make a copy of the class it will be changed automatically.
BasePotion
has two different constructors. In one of them you set the colors of the potion yourself, and in the other you just choose one of the "preset" colorings defined by the base game. In this example, we'll be using the one where you set the colors yourself. First, define the colors you'll be using.
public class MyPotion extends BasePotion {
public static final String ID = makeID(MyPotion.class.getSimpleName());
private static final Color LIQUID_COLOR = CardHelper.getColor(255, 0, 255);
private static final Color HYBRID_COLOR = CardHelper.getColor(255, 0, 255);
private static final Color SPOTS_COLOR = CardHelper.getColor(255, 0, 255);
CardHelper.getColor
is a helper method defined by the base game to create a color based on red/green/blue values from 0 to 255. You can define these colors in any way you want, whether using a preexisting color (eg. Color.GRAY.cpy();
) or using one of the constructors of the Color class (new Color(
).
It's important to note that which of these colors you need depends on the shape of the potion. Not all shapes support all three colors. In fact, if you provide a color that isn't supported, an error will occur and your potion will just turn into a Fire Potion. In these cases, you should be able to find an error message in the log explaining the problem, such as this:
In this example, the potion has a spots color, but the shape used doesn't support it. To fix this, you would just use null
for SPOTS_COLOR
.
private static final Color LIQUID_COLOR = CardHelper.getColor(255, 0, 255);
private static final Color HYBRID_COLOR = CardHelper.getColor(255, 0, 255);
private static final Color SPOTS_COLOR = null;
public MyPotion() {
super(ID, 5, PotionRarity.COMMON, PotionSize.MOON, LIQUID_COLOR, HYBRID_COLOR, SPOTS_COLOR);
}
The first parameter is just the ID.
The second parameter is the potion's potency
, the number that appears in its description and is affected by relics like Sacred Bark. If your potion doesn't have a number like this just use 0.
Third is the rarity.
Fourth is the shape. This determines the shape of the bottle and which of the color variables you can use. For a custom appearance, just use any of them and continue on until the end of this page.
Lastly, is the color. Here you'll either give it three colors you've chosen, or a PotionColor
, like PotionColor.FIRE
.
If you want the potion to be only for a specific character, add
public MyPotion() {
super(ID, 5, PotionRarity.COMMON, PotionSize.MOON, LIQUID_COLOR, HYBRID_COLOR, SPOTS_COLOR);
playerClass = AbstractPlayer.PlayerClass.IRONCLAD;
}
to the constructor. This would make this potion exclusive to the Ironclad (note that this is set up through the AutoAdd checking this field; normally you have to set the character when registering the potion instead).
If your potion is going to be thrown at an enemy, set isThrown = true;
and targetRequired = true;
. If it's just thrown but doesn't need a target (eg. Explosive Potion) just set isThrown
to true.
Right now, your potion class should look something like this:
Click on the class definition (the line with the error) and hit Alt+Enter. This should give you the option to "Implement methods".
Choose it. In the window appears, just leave it as-is and click Ok
.
This should create two empty methods inside the class, getDescription
and use
.
The text for potions is loaded from PotionStrings.json
, and accessed using the DESCRIPTIONS
array. You can use this method, defining the text in parts:
"${modID}:MyPotion": {
"NAME": "Some Kind of Potion",
"DESCRIPTIONS": [
"Gain #b",
" #yStrength."
]
}
And then put the description together in the getDescription
method. #b
is used to color the number that will be added there blue. (Updated 9/9/2024 - if the DESCRIPTIONS
field does not exist use potionStrings.DESCRIPTIONS
.)
@Override
public String getDescription() {
return DESCRIPTIONS[0] + potency + DESCRIPTIONS[1];
}
Slay the Spire tends to put descriptions together like this, but you can also use the String.format
method. In this case, you would use %d
in the potion's description text for where the number should go.
"${modID}:MyPotion": {
"NAME": "Some Kind of Potion",
"DESCRIPTIONS": [
"Gain #b%d #yStrength."
]
}
Then in getDescription
, you would use String.format
.
@Override
public String getDescription() {
return String.format(DESCRIPTIONS[0], potency);
}
In the use
method, you queue actions to do whatever you want the potion to do. In this case, gaining Strength. Since this potion doesn't target an enemy, target
is unused.
@Override
public void use(AbstractCreature target) {
if (AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT) {
addToBot(new ApplyPowerAction(AbstractDungeon.player, AbstractDungeon.player, new StrengthPower(AbstractDungeon.player, potency), potency));
}
}
The in-combat check if (AbstractDungeon.getCurrRoom().phase == AbstractRoom.RoomPhase.COMBAT)
may not be necessary.
Potions do not generate keyword tooltips on their own. If you need them (or any other additional tooltip), override the addAdditionalTips
method and add the tooltips to the "tips" ArrayList
.
@Override
public void addAdditionalTips() {
//Adding a tooltip for Strength
this.tips.add(new PowerTip(TipHelper.capitalize(GameDictionary.STRENGTH.NAMES[0]), GameDictionary.keywords.get(GameDictionary.STRENGTH.NAMES[0])));
}
When Slay the Spire starts and loads its localization, it stores most of its keyword text in the GameDictionary class. If you want to get some text from one of your custom keywords, this is how I recommend doing it for BasicMod.
Utilize the setContainerImg
/setOutlineImg
/setLiquidImg
/setHybridImg
/setSpotsImg
methods, along with TextureLoader.getTexture
. Use methods like imagePath
(defined in the main mod file) to create the path to the image in your resource files.
Images are rendered in the order outlineImg
, liquidImg
, hybridImg
, spotsImg
, containerImg
. liquidImg
, hybridImg
, and spotsImg
are rendered in the provided colors, if those colors are not null. containerImg
will appear the same as the original image. These images are expected to be 64x64.
To use a fixed image, you can pass null for hybridColor
and spotsColor
, use a blank image for setLiquidImg
(you can use TextureLoader.getTexture("images/blank.png")
for a blank image built into Slay the Spire), and then use setContainerImg
to the desired image. You should also setOutlineImg
to an outline of that image.
If your potion uses potency, Sacred Bark will work with no additional changes. If your potion does not use potency, you can manually check for Sacred Bark if you want or just don't if doubling the effect doesn't make sense.