Recognize
That's why it is a course on how to make a mod for Slay the Spire.
BaseMod wiki in Git https://github.com/daviscook477/BaseMod/wiki/Getting-Started-(For-Modders)
Mod list on the wiki in Mod The Spire's Git https://github.com/kiooeht/ModTheSpire/wiki/List-of-Known-Mods
Introduction to Slay the Spire Mod Development https://qiita.com/kojim/items/0c7164c78a5a909b4478 https://qiita.com/kojim/items/1a97f200fc8e545cee13 https://qiita.com/kojim/items/f61d9f3553e2d045aa2e
You can do it by looking at the above. Thanks to my ancestors.
I will explain "Getting Started With Modding Slay The Spire" on the BaseMod wiki because it will not increase the number of people who make it. The beginning is the construction of the development environment, but if you have a Java development environment, please read it diagonally. In the first place, this article is for super beginners.
First of all, you are told to create a folder. You can go anywhere, but please create a folder. MOD In a folder with the name I'm going to make a lot (such as my_mods). So, it is said that you can create a folder called "lib" in it, so create it. Put BaseMod.jar and ModTheSpire.jar there.
BaseMod.jar https://github.com/daviscook477/BaseMod/releases ModTheSpire.jar https://github.com/kiooeht/ModTheSpire/releases
BaseMod is a mod API, and Mod The Spire is required to run Slay the Spire with mods. Well both are needed. Yes. Oh, you can also enter from Steam Workshop. Let's put it in.
Then, in the same folder, find desktop-1.0.jar in the Slay the Spire folder and put it in it.
Now, regarding the item of "Environment Setup", since it is a development environment, please choose the one you like. It was written that it was easy, so I did it with Intel iJ, and I will only explain it after that. So first install Java SE Development Kit 8 and then InteliJ as well.
It says that you should create New Project after starting Intel iJ with. Select JDK1.8 and Maven Next, GroupID and ArtifactId should be the name of the mod you are about to create. The example is ExampleMod. Finally, finish so that the project file is created in the folder created earlier.
Well, then suddenly an important thing is written, but I think that pom.xml is directly under the project folder, so edit it.
Example Mod pom.xml https://gist.github.com/alexdriedger/fb74397086ee80073417f19d6305bb05
Here, the locations of the three jar files that you put in the lib folder and their versions are specified. Be careful if the directory structure is different from the example.
Also, I think it was necessary to partially edit the package from line 54 here. Please write it because it is the output destination after building.
When you finish editing the pom, you will see a pop-up saying "Maven projects need to be imported", so press Import Changes to reflect it. Then press Ctrl + Alt + Shift + S to bring up the Structure screen, select Modules, and there are 3 Mavens, so check it and Apply.
Now, if you double-click "Lifecycle> package" in the window opened by "View> Tool Windows> Maven Projects", you can build it and a .jar file will be created in the target folder.
The character class of Slay the Spire is called COLOR, Iron Clad is RED, Silent is GREEN, and Defect is BLUE. If you want to make a new character, you have to add this COLOR first (although it would be easier if you just put an additional card for these guys).
Custom Colors https://github.com/daviscook477/BaseMod/wiki/Custom-Colors
It's difficult to read and understand this much, so I'll explain it myself from here on. Let's go one by one. First, add colors and cards. I'm not sure if it was added only in color, so I can add cards at once.
Also, it's annoying to explain, so if you don't understand well, please see the following repository. It's a hassle to prepare images, so please start from here. https://github.com/levelnineteen/stsmod
I think that the structure is like this when the project is created, but the class (program part) is placed under java, and the text and images are placed under resource.
examle_mod +.idea +src -+main --+java --+resource
First, create a directory with the mod name under java, and a directory called patches under it, and create the following two classes.
examle_mod +.idea +src -+main --+java ---+example_mod ----+patches --+resource
AbstractCardEnum.java
package example_mod.patches; //I think it will enter here automatically
import com.evacipated.cardcrawl.modthespire.lib.SpireEnum;
import com.megacrit.cardcrawl.cards.AbstractCard;
public class AbstractCardEnum {
@SpireEnum
public static AbstractCard.CardColor EXAMPLE_COLOR; //Favorite variable name. I will use it forever.
}
LibraryTypeEnum.java
package example_mod.patches; //I think it will enter here automatically
import com.evacipated.cardcrawl.modthespire.lib.SpireEnum;
import com.megacrit.cardcrawl.helpers.CardLibrary;
public class LibraryTypeEnum {
@SpireEnum
public static CardLibrary.LibraryType EXAMPLE_COLOR; //Same as above. I will use it forever.
}
Now that the preparations are complete, create Main.java or MOD name.java class directly under java / example_mod so that it is easy to understand.
Main.java
import basemod.BaseMod;
import basemod.interfaces.*;
import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer;
import com.megacrit.cardcrawl.core.Settings;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.megacrit.cardcrawl.helpers.CardHelper;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.localization.CharacterStrings;
import example_mod.patches.*; //I'll read the patch I made earlier
@SpireInitializer
public class Main implements
EditCardsSubscriber, //Implement when adding a card
EditStringsSubscriber, //Implement when reading a language file
{
//First, there are variables for basic color settings.
private static final Color EXAMPLE_COLOR_BG = CardHelper.getColor(100.0f, 50.0f, 50.0f); //This is the background color of the bar when it appears in the card list selection.
private static final String ATTACK_EXAMPLE = "img/cards/bg_attack_512.png "; //This area will be explained later
private static final String SKILL_EXAMPLE = "img/cards/bg_skill_512.png "; //Please write the variable name for the time being
private static final String POWER_EXAMPLE = "img/cards/bg_power_512.png ";
private static final String ENERGY_ORB_EXAMPLE = "img/cards/orb_512.png ";
private static final String ATTACK_PORT_EXAMPLE = "img/cards/bg_attack_1024.png ";
private static final String SKILL_PORT_EXAMPLE = "img/cards/bg_skill_1024.png ";
private static final String POWER_PORT_EXAMPLE = "img/cards/bg_power_1024.png ";
private static final String ENERGY_ORB_PORT_EXAMPLE = "img/cards/orb_1024.png ";
private static final String ENERGY_ORB_CARD_EXAMPLE = "img/cards/orb_ui.png ";
public Main(){
BaseMod.subscribe(this);
BaseMod.addColor(
//It is a color addition part. Just enter the variables set above.
AbstractCardEnum.EXAMPLE_COLOR //color
, EXAMPLE_COLOR_BG //bgColor
, EXAMPLE_COLOR_BG//backColor
, EXAMPLE_COLOR_BG//frameColor
, EXAMPLE_COLOR_BG//frameOutlineColor
, EXAMPLE_COLOR_BG//descBoxColor
, EXAMPLE_COLOR_BG //trailVfColor
, EXAMPLE_COLOR_BG//glowColor
, ATTACK_EXAMPLE//attackBg
, SKILL_EXAMPLE//skillBg
, POWER_EXAMPLE//powerBG
, ENERGY_ORB_EXAMPLE//energyOrb
, ATTACK_PORT_EXAMPLE//attackBgPortrait
, SKILL_PORT_EXAMPLE//skillBgPortrait
, POWER_PORT_EXAMPLE//powerBgPortrait
, ENERGY_ORB_PORT_EXAMPLE//energyOrbPortrait
, ENERGY_ORB_CARD_EXAMPLE//CardEnergyOrb
);
}
public static void initialize() {
Main main = new Main();
}
@Override
public void receiveEditStrings() {
//Language files will be explained later.
BaseMod.loadCustomStringsFile(CardStrings.class, "localization/cards-" + Settings.language + ".json");
}
@Override
public void receiveEditCards() {
//This is the card addition part. This will also be explained later.
BaseMod.addCard(new example_mod.cards.TestAttack());
}
}
I still have something to write. Because I have to write the contents of the essential card. Prepare a directory called cards directly under java / example_mod and put the following classes.
TestAttack.java
package example_mod.cards; //Well here should enter automatically
import basemod.abstracts.CustomCard;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.cards.DamageInfo;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import example_mod.patches.AbstractCardEnum; //Card color cannot be handled unless this is imported
public class TestAttack extends CustomCard {
public static final String ID = "examplemod:TestAttack"; //Don't make a mistake because it is the reference ID of the language file.
private static CardStrings cardStrings = CardCrawlGame.languagePack.getCardStrings(ID);
public static final String NAME = cardStrings.NAME;
public static final String DESCRIPTION = cardStrings.DESCRIPTION;
public static final String IMG_PATH = "img/cards/card.png "; //It is a picture of the card. I will explain later.
private static final int COST = 0; //Energy cost
private static final int ATTACK_DMG = 4; //damage
private static final int UPGRADE_PLUS_DMG = 3; //Damage when upgrading
public TestAttack() {
super(ID, NAME, IMG_PATH, COST, DESCRIPTION,
CardType.ATTACK, //Card type
AbstractCardEnum.EXAMPLE_COLOR, //The card color variable that I have been using since a while ago. Don't make a mistake
CardRarity.COMMON, //Card rarity
CardTarget.ENEMY //Who is the target
);
this.damage=this.baseDamage = ATTACK_DMG;
}
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
//Cause damage
AbstractDungeon.actionManager.addToBottom(
new com.megacrit.cardcrawl.actions.common.DamageAction(
m,
new DamageInfo(p, this.damage, this.damageTypeForTurn),
AbstractGameAction.AttackEffect.SLASH_DIAGONAL) //Screen effect
);
}
@Override
public AbstractCard makeCopy() {
return new TestAttack();
}
//Processing at the time of card upgrade
@Override
public void upgrade() {
if (!this.upgraded) {
upgradeName();
upgradeDamage(UPGRADE_PLUS_DMG);
}
}
}
Yes, thank you for your hard work. There are many things to do just by adding a new type of card. There are still.
examle_mod +.idea +src -+main --+java ---Main.java ---+example_mod ----+cards -----TestAttack.java ----+paches -----LibraryTypeEnum.java -----AbstractCardEnum.java --+resource
Now we need to add the image and language files to the resource. Create an img / cards directory and a localization directory under resource. The following files are required in img / cards. All are transparent png files.
bg_attack_512.png Background of the attack card. 512x512 bg_skill_512.png Skill card background. 512x512 bg_power_512.png Power card background. 512x512 You don't have to prepare the band under the card name. orb_512.png Energy orb. Since it can be placed on a 512x512 card, it must be drawn small in the upper left. bg_attack_1024.png This area should be expanded from 512 to 1024x1024. bg_skill_1024.png bg_power_1024.png orb_1024.png However, this guy must not enlarge 512 (the center point is different). Well, I think it's better to reduce each of these large ones. orb_ui.png Display when probably used in tooltips. 23x23 card.png Card design. Originally, it is easy to match with the card name (card class name). Well because it's a test. 250x190 card_p.png A large version of the design. You can enlarge the above. 500x380. Since it is used automatically, it is not specified in any class.
The following json files are required in localization.
cards-JPN.json
{
"examplemod:TestAttack": { //Mod identifier:Write with the card name. Otherwise, I'll wear it.
"NAME": "card name",
"DESCRIPTION": "A description of the card.!d!If you write, the damage value will come out, but please check there"
}
}
Well, it's made overseas, so please make cards-ENG.json and prepare it in English.
I forgot, but if there is no json below directly under resources, various things will be said when selecting a mod, so please put it in.
ModTheSpire.json
{
"modid": "examplemod",
"name": "examplemod",
"author_list": ["yourname"],
"description": "",
"version": "0.0.1",
"sts_version": "01-01-2019", //Which version of Slay the Spire should be higher?
"mts_version": "3.6.0", //I think it's a story about which version of Mod the Spire should be higher, so it's appropriate
"dependencies": ["basemod"],
"update_json": ""
}
thank you for your hard work. It should now work.
--+java ---Main.java ---+example_mod ----+cards -----TestAttack.java ----+paches -----LibraryTypeEnum.java -----AbstractCardEnum.java --+resource ---ModTheSpire.json ---+img ----+cards ----- Full of images for cards ---+localization ----cards-JPN.json ----cards-ENG.json
If you double-click "Lifecycle> package" in the window opened by "View> Tool Windows> Maven Projects", you can build it and you will have a .jar file in the target folder. Please deal with the error.
Create a folder called mods in steamapps \ common \ SlayTheSpire in the Steam library. Put the example_mod.jar you created earlier and BaseMod.jar in this mod.
Directly under steamapps \ common \ SlayTheSpire, put MTS.cmd that was inside when you unzipped ModTheSpire.zip. Mac people seem to prefer .sh.
So, when I run MTS.cmd,
So, check BaseMod and example_mod, then check Debug and press Play. Somehow example_mod is gray and cannot be selected because the mts_version specified in ModTheSpire.json is wrong.
If it starts up safely, example_mod is added to the card library, and a cool card is added, it is successful so far. Thank you for your hard work.
If you want to add a character, you need a starter relic, and the card also needs power in addition to skill. So let's go all at once. From the addition of relics. Create a relics directory under java / example_mod and create the following classes.
TestRelic.java
package example_mod.relics;
import basemod.abstracts.CustomRelic;
import com.megacrit.cardcrawl.helpers.ImageMaster;
import com.megacrit.cardcrawl.relics.AbstractRelic;
//Enter by ability
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.powers.RegenPower;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.actions.common.RelicAboveCreatureAction;
public class TestRelic extends CustomRelic {
public static final String ID = "examplemod:TestRelic";
public static final String IMG = "img/relics/TestRelic.png ";
public static final String OUTLINE_IMG = "img/relics/outline/TestRelic.png ";
public TestRelic(){
super(
ID,
ImageMaster.loadImage(IMG),
ImageMaster.loadImage(OUTLINE_IMG),
RelicTier.STARTER, //Rarity. Here is the starter relic
LandingSound.FLAT
);
}
public String getUpdatedDescription(){
return DESCRIPTIONS[0];
}
@Override
public AbstractRelic makeCopy(){
return new TestRelic();
}
//Get 5 plays at the beginning of the battle
@Override
public void onEquip() {
AbstractDungeon.rareRelicPool.remove("Dead Branch");
}
public void atBattleStart() {
AbstractDungeon.actionManager.addToBottom(
new RelicAboveCreatureAction(AbstractDungeon.player, this)
);
AbstractDungeon.actionManager.addToBottom(
new ApplyPowerAction(
AbstractDungeon.player,
AbstractDungeon.player,
new RegenPower(AbstractDungeon.player, 5),
5
)
);
}
}
Create a relics directory under resources / img and prepare the following image files.
TestRelic.png 128x128 transparent png
In addition, create an outline directory under the relics directory and prepare the following image files.
TestRelic.png 128x128 transparent png. The one with the image in the above directory painted white.
Now, we also need a file for text, so prepare the following file in resources / localization.
relics-JPN.json
{
"examplemod:TestRelic": {
"NAME": "Relic name",
"FLAVOR": "Flavor text",
"DESCRIPTIONS": [
"Explanatory text. Note that it is plural with S. When putting a variable in the description, decompose it and put it in between"
]
}
}
Since it is made overseas, please make relics-ENG.json and prepare it in English.
Next, add power. The power itself is added, and the card that gains power is added. Create a powers directory under example_mod and create the following classes.
TestPowerGold.java
package example_mod.powers;
import com.badlogic.gdx.graphics.Texture;
import com.megacrit.cardcrawl.core.AbstractCreature;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.powers.AbstractPower;
import com.megacrit.cardcrawl.localization.PowerStrings;
public class TestPowerGold extends AbstractPower {
public static final String POWER_ID = "examplemod:TestPowerGold";
private static final PowerStrings powerStrings =
CardCrawlGame.languagePack.getPowerStrings(POWER_ID);
public static final String NAME = powerStrings.NAME;
public static final String[] DESCRIPTIONS = powerStrings.DESCRIPTIONS;
public TestPowerGold(AbstractCreature owner, int amount) {
this.name = NAME;
this.ID = POWER_ID;
this.owner = owner;
this.amount = amount;
this.type = AbstractPower.PowerType.BUFF;
updateDescription();
this.img = new Texture("img/powers/TestPower.png ");
}
public void updateDescription() {
this.description = (DESCRIPTIONS[0] + this.amount + DESCRIPTIONS[1]); //This is how the description is divided.
}
}
You will also need a text file, so prepare the following files in resources / localization.
powers-JPN.json
{
"examplemod:TestPowerGold": {
"NAME": "Power name",
"DESCRIPTIONS": [
"Gold at the end of the battle",
"Get"
]
}
}
What is prepared here is a model of power. The specific power needs to be written on the card. Prepare the following classes in example_mod / cards.
TestPower.java
package example_mod.cards;
import basemod.abstracts.CustomCard;
import com.megacrit.cardcrawl.actions.common.ApplyPowerAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import example_mod.patches.AbstractCardEnum;
import example_mod.powers.*; //The one with the power I made earlier. Because it's annoying*
import com.megacrit.cardcrawl.rooms.AbstractRoom.RoomPhase;
public class TestPower extends CustomCard {
public static final String ID = "examplemod:TestPower";
private static CardStrings cardStrings = CardCrawlGame.languagePack.getCardStrings(ID);
public static final String NAME = cardStrings.NAME;
public static final String DESCRIPTION = cardStrings.DESCRIPTION;
public static final String IMG_PATH = "img/cards/card.png "; //Image of the card. I will use it for the time being
private static final int COST = 0; //Energy cost
//Card-specific variables
private static final int GETMONEY = 100;
private static final int UPGRADE_GETMONEY = 10;
public TestPower() {
super(ID, NAME, IMG_PATH, COST, DESCRIPTION,
CardType.POWER,
AbstractCardEnum.EXAMPLE_COLOR, //I say it many times, but the color name
CardRarity.COMMON, //Rarity
CardTarget.SELF //The target of the card. Power is about SELF
);
this.magicNumber = this.baseMagicNumber = GETMONEY;
}
@Override
public void use(AbstractPlayer p, AbstractMonster m) {
//The ability to get money at the end of the battle
if (AbstractDungeon.getCurrRoom().phase == RoomPhase.COMBAT) {
AbstractDungeon.getCurrRoom().addGoldToRewards(this.magicNumber);
AbstractDungeon.actionManager.addToBottom(
new ApplyPowerAction(
p,
p,
new TestPowerGold(p, this.magicNumber), //The one with the power I just made
this.magicNumber
)
);
}
}
@Override
public AbstractCard makeCopy() {
return new TestPower();
}
//Processing at the time of card upgrade
@Override
public void upgrade() {
if (!this.upgraded) {
this.upgradeName();
this.upgradeDamage(UPGRADE_GETMONEY);
}
}
}
I also need a power icon image. Create a powers directory under resources / img and prepare the following image files.
TestPower.png 32x32 transparent file. This is the only power.
--+java ---Main.java ---+example_mod ----+cards -----TestAttack.java -----TestPower.java ----+relics -----TestRelic.java ----+paches -----LibraryTypeEnum.java -----AbstractCardEnum.java ----+powers -----TestPowerGold.java --+resource ---ModTheSpire.json ---+img ----+cards ----- Full of images for cards ----+relics -----TestRelic.png -----+outline ------TestRelic.png ----+powers -----TestPower.png ---+localization ----cards-JPN.json ----cards-ENG.json ----relics-JPN.json ----relics-ENG.json ----powers-JPN.json ----powers-ENG.json
Well, this alone will not work. It is necessary to add to example_mod / Main.java.
//The following additions are required for import.
import com.megacrit.cardcrawl.localization.RelicStrings;
import com.megacrit.cardcrawl.localization.PowerStrings;
import example_mod.relics.*;
//More places to implements
public class Main implements
PostInitializeSubscriber, //Is it a badge or something to display?
EditCardsSubscriber, //Implement when adding a card
EditRelicsSubscriber, //Implementation when adding a relic
EditStringsSubscriber, //Implement when reading a language file
{
//Increase language files
@Override
public void receiveEditStrings() {
BaseMod.loadCustomStringsFile(CardStrings.class, "localization/cards-" + Settings.language + ".json");
BaseMod.loadCustomStringsFile(RelicStrings.class, "localization/relics-" + Settings.language + ".json");
BaseMod.loadCustomStringsFile(PowerStrings.class, "localization/powers-" + Settings.language + ".json");
}
//Increase the number of cards to add
@Override
public void receiveEditCards() {
BaseMod.addCard(new training_mod.cards.TestAttack());
BaseMod.addCard(new training_mod.cards.TestPower());
}
//Add relic
public void receiveEditRelics() {
//Add relic
BaseMod.addRelicToCustomPool(
new TestRelic(),
AbstractCardEnum.EXAMPLE_COLOR //Add to color. Common relic becomes another method
);
}
If you build and the power card is in the card list and it is displayed in the starter relic in the relic list, it is successful. There is a skill card creation though I have not explained it, but you can do it for the time being just by changing the CardType of the attack to CardType.SKILL.
Sorry I made you wait. Let's make a character. Thinking normally, you need a character image first, so let's make it. According to BaseMod's explanation, it says that you can use Sprite. The explanation after that is the setting by the image set made with Spine. …… Spine is paid software, so let's go with Spriter.
Download from here. https://brashmonkey.com/download_spriter/
After installing it, please take a look at the following tutorial for the time being. https://www.youtube.com/watch?v=aQy7eX_CWPM&list=PL8Vej0NhCcI5sxOT64-Z31LW59VUyAVfr&t=0s&index=2
It is a fun software that allows you to create a transparent png file for each part (arms, legs, etc.), place it in Sprite, and animate it on the timeline.
It's too fun to do, so first create a char / anim directory under img and then create a Sprite project under it. Well, I think the project name such as exampleChar is fine. Place the character image in it.
--+java ---Main.java ---+example_mod ----+cards ----+relics ----+paches ----+powers --+resource ---+img ----+char -----+anim ------ CharacterImage.png 512x512 transparent png file
Then CharacterImage.png will be visible on the Splitter, so double-click to determine the reference point, select directly below, throw in the image and place it at x0, y0.
It's okay, it's appropriate ... I don't usually animate it, and you haven't seen it, right? STS character.
I need more images than that. Please prepare the following.
--+java ---Main.java ---+example_mod ----+cards ----+relics ----+paches ----+powers --+resource ---+img ----+char ----- ExampleCorpse.png 512x512 transparent png file. It's the one when he died. ----- ExampleShoulder.png 1920x1136 transparent png file. It is an image from behind that is displayed at camps. -----+anim ------ CharacterImage.png 512x512 transparent png file. A little while ago ------ ExampleChar.scml File saved by Sprite. Information such as placement and animation. I will use it after this. ----+charSelect ----- ExampleButton.png 200x200 transparent png file. Button to press on the character selection screen ----- ExamplePortrait.png 1920x1200 transparent png file. The background that appears after you press the button above. Nice to meet you.
Good work. The materials are now complete. Let's add characters. First, add the following class to example_mod / paches.
ExampleClassEnum.java
package example_mod.patches;
import com.evacipated.cardcrawl.modthespire.lib.SpireEnum;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
//The example of the class name can be the name of the mod you make.
public class ExampleClassEnum {
@SpireEnum
public static AbstractPlayer.PlayerClass ExampleClass;
}
Create an example_mod / character directory and add the following classes in it.
ExampleChar.java
package example_mod.character;
//I've imported it even though I'm not using it, but I think there will come a time when I need something.
import java.util.ArrayList;
import java.util.List;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import basemod.abstracts.CustomPlayer;
import basemod.animations.SpriterAnimation;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.core.EnergyManager;
import com.megacrit.cardcrawl.cutscenes.CutscenePanel;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.events.city.Vampires;
import com.megacrit.cardcrawl.helpers.*;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.localization.CharacterStrings;
import com.megacrit.cardcrawl.screens.CharSelectInfo;
import com.megacrit.cardcrawl.screens.stats.CharStat;
import com.megacrit.cardcrawl.unlock.UnlockTracker;
import com.megacrit.cardcrawl.actions.AbstractGameAction.AttackEffect;
import example_mod.cards.*;
import example_mod.relics.*;
import example_mod.patches.*;
public class TrainingChar extends CustomPlayer {
public static final CharacterStrings charStrings = CardCrawlGame.languagePack.getCharacterString("examplemod:ExampleChar");
public static final int ENERGY_PER_TURN = 3; //How much energy can I get for one turn?
public static final String EXAMPLECHAR_SHOULDER_2 = "img/char/ExampleShoulder.png "; //Behind the scenes at the camp
public static final String EXAMPLECHAR_SHOULDER_1 = "img/char/ExampleShoulder.png "; //Other behind images. If you want to divide it, make it.
public static final String EXAMPLECHAR_CORPSE = "img/char/ExampleCorpse.png "; //Corpse image
public ExampleChar(String name){
super(name,
ExampleClassEnum.ExampleClass,
null,
null,
null,
new SpriterAnimation("img/char/anim/ExampleChar.scml") //Files created with Spriter
);
initializeClass(null,
EXAMPLECHAR_SHOULDER_2,
EXAMPLECHAR_SHOULDER_1,
EXAMPLECHAR_CORPSE,
getLoadout(),
20.0F,
-10.0F,
220.0F,
290.0F,
new EnergyManager(ENERGY_PER_TURN)
);
}
@Override
public String getTitle(AbstractPlayer.PlayerClass playerClass){
return charStrings.NAMES[0];
}
@Override
public String getSpireHeartText(){
return charStrings.TEXT[0];
}
@Override
public String getLocalizedCharacterName() {
return charStrings.NAMES[0]; //I don't know where the above is different. Is it okay to be the same?
}
@Override
public String getVampireText() {
//A vampire event. I think 0 is iron clad and 1 is silent, but I don't know.
return Vampires.DESCRIPTIONS[0];
}
public Color getCardRenderColor() {
return CardHelper.getColor(100.0f, 50.0f, 50.0f); //EXAMPLE_COLOR_Is it the same as BG?
}
public Color getCardTrailColor() {
return CardHelper.getColor(100.0f, 50.0f, 50.0f); //EXAMPLE_COLOR_Is it the same as BG?
}
public Color getSlashAttackColor() {
return CardHelper.getColor(100.0f, 50.0f, 50.0f); //EXAMPLE_COLOR_Is it the same as BG?
}
public AttackEffect[] getSpireHeartSlashEffect() {
//It seems to be an effect, but I'm not sure what it is.
return new AttackEffect[]{
AttackEffect.SLASH_HEAVY,
AttackEffect.FIRE,
AttackEffect.SLASH_DIAGONAL,
AttackEffect.SLASH_HEAVY,
AttackEffect.FIRE,
AttackEffect.SLASH_DIAGONAL
};
}
public AbstractCard getStartCardForEvent() {
return new TestPower() ; //I don't know what it is for, but for the time being
}
public BitmapFont getEnergyNumFont() {
return FontHelper.energyNumFontBlue;
}
public void doCharSelectScreenSelectEffect() {
//I think it's the sound and screen effect when you select a character
CardCrawlGame.sound.playV("AUTOMATON_ORB_SPAWN", 1.75f);
CardCrawlGame.screenShake.shake(ScreenShake.ShakeIntensity.LOW, ScreenShake.ShakeDur.SHORT, true);
}
public String getCustomModeCharacterButtonSoundKey() {
return "AUTOMATON_ORB_SPAWN";
}
public AbstractCard.CardColor getCardColor() {
//The color of the card used for this character
return AbstractCardEnum.EXAMPLE_COLOR; //Card color variable. Make no mistake.
}
public AbstractPlayer newInstance() {
return new ExampleChar(this.name);
}
public ArrayList<String> getStartingDeck() { //The contents of the initial deck. Put the basic BASIC rarity. as you like.
ArrayList<String> retVal = new ArrayList<>();
retVal.add(TestAttack.ID);
retVal.add(TestAttack.ID);
retVal.add(TestAttack.ID);
retVal.add(TestAttack.ID);
retVal.add(TestAttack.ID);
retVal.add(TestAttack.ID);
retVal.add(TestAttack.ID);
retVal.add(TestAttack.ID);
retVal.add(TestAttack.ID);
return retVal;
}
public ArrayList<String> getStartingRelics() { //Designation of starter relic.
ArrayList<String> retVal = new ArrayList<>();
retVal.add(TestRelic.ID);
UnlockTracker.markRelicAsSeen(TestRelic.ID);
return retVal;
}
//The part that determines the character ability
private static final int STARTING_HP = 75;
private static final int MAX_HP = 75;
private static final int STARTING_GOLD = 99;
private static final int HAND_SIZE = 5;
private static final int ORB_SLOTS = 0;
private static final int ASCENSION_MAX_HP_LOSS = 5;
public int getAscensionMaxHPLoss() {
return ASCENSION_MAX_HP_LOSS;
}
public CharSelectInfo getLoadout() { //I think it's the information that appears on the character selection screen
return new CharSelectInfo(
charStrings.NAMES[0],
charStrings.TEXT[0],
STARTING_HP,
MAX_HP,
ORB_SLOTS,
STARTING_GOLD,
HAND_SIZE,
this,
getStartingRelics(),
getStartingDeck(),
false);
}
}
Add the following files to resources / localization.
char-JPN.json
{
"examplemod:ExampleChar": {
"NAMES": ["Character name"],
"TEXT": ["Explanatory text. If you want to change it depending on the location, divide it"]
}
}
Since it is an overseas ry, please also prepare char-ENG.json.
Now, the final edit of example_mod / Main.java.
Main.java
import basemod.BaseMod;
import basemod.interfaces.*;
import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer;
import com.megacrit.cardcrawl.core.Settings;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.megacrit.cardcrawl.helpers.CardHelper;
import com.megacrit.cardcrawl.localization.CardStrings;
import com.megacrit.cardcrawl.localization.CharacterStrings;
import com.megacrit.cardcrawl.localization.RelicStrings;
import com.megacrit.cardcrawl.localization.PowerStrings;
//This is when you want to output debug log
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import example_mod.patches.*;
import example_mod.relics.*;
@SpireInitializer
public class Main implements
EditCardsSubscriber, //Implement when adding a card
EditRelicsSubscriber, //Implementation when adding a relic
EditStringsSubscriber, //Implement when reading a language file
EditCharactersSubscriber //Implement when adding a character
{
private static final Color EXAMPLE_COLOR_BG = CardHelper.getColor(100.0f, 50.0f, 50.0f);
private static final String ATTACK_EXAMPLE = "img/cards/bg_attack_512.png ";
private static final String SKILL_EXAMPLE = "img/cards/bg_skill_512.png ";
private static final String POWER_EXAMPLE = "img/cards/bg_power_512.png ";
private static final String ENERGY_ORB_EXAMPLE = "img/cards/orb_512.png ";
private static final String ATTACK_PORT_EXAMPLE = "img/cards/bg_attack_1024.png ";
private static final String SKILL_PORT_EXAMPLE = "img/cards/bg_skill_1024.png ";
private static final String POWER_PORT_EXAMPLE = "img/cards/bg_power_1024.png ";
private static final String ENERGY_ORB_PORT_EXAMPLE = "img/cards/orb_1024.png ";
private static final String ENERGY_ORB_CARD_EXAMPLE = "img/cards/orb_ui.png ";
//When you want to output a debug log.
public static final Logger logger = LogManager.getLogger(Main.class.getName());
public Main(){
BaseMod.subscribe(this);
BaseMod.addColor(
AbstractCardEnum.EXAMPLE_COLOR //Color variables
, EXAMPLE_COLOR_BG //bgColor
, EXAMPLE_COLOR_BG//backColor
, EXAMPLE_COLOR_BG//frameColor
, EXAMPLE_COLOR_BG//frameOutlineColor
, EXAMPLE_COLOR_BG//descBoxColor
, EXAMPLE_COLOR_BG //trailVfColor
, EXAMPLE_COLOR_BG//glowColor
, ATTACK_EXAMPLE//attackBg
, SKILL_EXAMPLE//skillBg
, POWER_EXAMPLE//powerBG
, ENERGY_ORB_EXAMPLE//energyOrb
, ATTACK_PORT_EXAMPLE//attackBgPortrait
, SKILL_PORT_EXAMPLE//skillBgPortrait
, POWER_PORT_EXAMPLE//powerBgPortrait
, ENERGY_ORB_PORT_EXAMPLE//energyOrbPortrait
, ENERGY_ORB_CARD_EXAMPLE//CardEnergyOrb
);
}
public static void initialize() {
Main main = new Main();
}
@Override
public void receiveEditStrings() {
BaseMod.loadCustomStringsFile(CardStrings.class, "localization/cards-" + Settings.language + ".json");
BaseMod.loadCustomStringsFile(RelicStrings.class, "localization/relics-" + Settings.language + ".json");
BaseMod.loadCustomStringsFile(CharacterStrings.class, "localization/char-" + Settings.language + ".json");
BaseMod.loadCustomStringsFile(PowerStrings.class, "localization/powers-" + Settings.language + ".json");
}
@Override
public void receiveEditCards() {
BaseMod.addCard(new example_mod.cards.TestAttack());
BaseMod.addCard(new example_mod.cards.TestPower());
//I will explain later
}
@Override
public void receiveEditCharacters() {
//Addition of uniquely defined characters
BaseMod.addCharacter(
new example_mod.character.ExampleChar("ExampleCharacter"),
"img/charSelect/ExampleButton.png ",
"img/charSelect/ExamplePortrait.png ",
ExampleClassEnum.ExampleClass
);
}
public void receiveEditRelics() {
//Add relic
logger.info("begin editing relics"); //It is like this to output the debug log.
BaseMod.addRelicToCustomPool(
new TestRelic(),
AbstractCardEnum.EXAMPLE_COLOR //Add to color. Common relic is another method
);
logger.info("done editing relics");
}
}
Now, build it, select a character, and start the game, and you're done! Thank you for your hard work!
Oh, that's because there aren't enough cards. Will you randomly display 3 cards at the end of the battle? If you proceeded like this now, there are only two types, so it will crash. I think that the debug log is also stopped at that point.
Then I don't know how many characters are needed for the character mod (please tell me).
I don't know, but the card selection is usually 3 cards, it may be increased by +1 by relic, and when the boss is defeated, all are rare Well, if there are 4 types of attack skill power COMMON, UNCOMMON, and RARE, it will work at least. 3 categories x 3 rare x 4 types, 36 sheets? If you look at other people's mods, there are about 70 or so ...
For the time being, I increased the production of cards with the same ability in my repository so that it works, so I think it has become a model.
It was embarrassing why the repository was "training" instead of "example". I wonder if the example is the same as BaseMod's explanation.
that's all.
Other notes ・ If you draw a card that is not normally available due to the effect, prepare a card color for it and make the rarity SPECIAL? -Prepare "UPGRADE_DESCRIPTION" when changing the description by upgrading.
Recommended Posts