Warning
Most of this no longer applies as of 4.0.0. An updated version of this wiki is in progress: https://hs.lunartides.dev/guides/creating/cards/generators/custom/ (#447)
Adding cards can be difficult, if you're going to add a card with logic, you should have some experience with TypeScript.
In order to add a card. It is highly recommended to use the Card Creator Tool.
If you plan on adding a card that is already implemented in vanilla Hearthstone. Skip this section and go to "Adding a Vanilla card"
Adding a Custom card
- Open the Hearthstone.js Runner again by opening "run.bat" in the Hearthstone.js folder.
- Type "m" to enable Developer Mode.
- Type "c" to open the card creator.
- Type "c" again to make a custom card.
Making a card with the card creator
Making a Minion
Typeneeds to beMinion.Nameis the name of the card.Textcan be whatever you want. In the picture above i use<b>Battlecry:</b> If.... The<b>means Bold and the</b>stops the bolding. There are a lot more tags like this, so keep that in mind. You don't have to use tags, it just makes it look better.Costis how much the card should cost, usually in mana.Classesis the class the minion belongs to. Example:Priest,Hunter,Mage, etc... Use commas (,) to add multiple classes.Rarityis the rarity of the card. Keep your spelling in mind.Keywordsis the keywords a minion has. Example:Taunt,Rush,Divine Spirit, etc... Your input should look like this:Keyword1, Keyword2, Keyword3, ....Attackis the amount of attack that the minion has.Healthis the amount of health that the minion has.Tribesis the text in the middle of the attack and health of a minion in Hearthstone. This can beDragon,Naga,Beast, etc... Your input should look like this:Tribe1, Tribe2, Tribe3, ....Uncollectibleis if the card should not be allowed in a deck. Uncollectible cards can still be explicitly summoned by other minions.
After all this has been filled out, it will show you where the card file was created. If you have VSCode installed, it will open it to that location (You can change the default editor in the config.ts file under General > Editor). If not you need to navigate to the card file and open it with your favorite code editor.
Making a Spell
If a field isn't shown here, it means it works the same as in "Making a Minion".
Typeneeds to beSpell.Spell Schoolsis a spells version of aTribe. This can beHoly,Shadow,Fire, etc... Your input should look like this:SpellSchool1, SpellSchool2, SpellSchool3, ....
Adding a Vanilla card
- Open the Hearthstone.js Runner again by opening "run.bat" in the Hearthstone.js folder.
- Type "m" to enable Developer Mode.
- Type "c" to open the card creator.
- Type "v" to make a vanilla card.
- Type in the name or
dbfIDof the card as it is in Hearthstone. - If there are multiple cards with the same name, it will ask you to choose one.
- Open the file created in your favorite code editor.
- Go to "Editing Logic"
Editing Logic
I would heavily recommend checking out the example cards at
cards/Examples/. They have a lot more thought put into them than this wiki, and go into more detail about more specific things.
This picture is outdated and uses
plrinstead ofownerandtribeinstead of `tribes.
After you have the card open in your favorite code editor, it is time to work on the logic. The card creator should have been able to detect if you want a battlecry, deathrattle, cast, etc... from the description of the card.
The self variable is the actual card played, the owner variable is the player the card belongs to, and the global game variable is the entire game (you can also access the functions module from this). Look at the source code for different things you can do.
More accurately:
- The
selfvariable is an instance of theCardclass as defined insrc/card.ts. - The
ownervariable is an instance of thePlayerclass as defined insrc/player.ts, look in there for the different functions you can call.
Example
If you want more examples, there is an example folder in the cards folder that shows you some basics. I highly recommend checking that out. If there is anything you need that isn't shown in the example cards, consult the cards that are already implemented. You can also open an issue / discussion to ask me directly.
One
Lets say you have a minion with the description Battlecry: Draw a card. The code for that would be:
async battlecry(self, owner) {
// Draw 1 card.
await owner.drawCards(1);
}
Two
Lets say you have a minion with the description Battlecry: Deal 2 damage to a minion. The code for that would be:
async battlecry(self, owner) {
// `game` is a global variable that you can always use.
// targetCard(prompt, theCardThatCalledTheFunction, flags)
const target = await game.prompt.targetCard("Deal 2 damage to a minion.", self);
// If you didn't select a target (which means they cancelled the selection), refund the card.
if (!target) {
return Card.REFUND;
}
await game.attack(2, target); // Deal 2 damage to the target
}
Three
Lets say you have a spell with the description Discover a minion that costs (5) or more. The code for that would be:
async cast(self, owner) {
// Discover needs a list to choose the 3 cards from.
// This will get all the cards currently in the game, excluding uncollectible cards.
let list = await Card.all();
// Filter the cards so that only minions that cost 5 or more remains.
list = list.filter(card => card.type === CardType.Minion && card.cost >= 5);
// If there are no cards that meet this criteria, stop execution.
if (list.length == 0) {
return;
}
// Discover filters out cards that arent from the player's class. Example: If the player is a Priest, only include Priest and Neutral cards. You can disable this using the flag after `list`.
// discover(prompt, listOfCardsToChooseFrom, filterOutCardsWithANonMatchingClass = true, amountOfCardsToChooseFrom = 3)
let minion = await game.prompt.discover("Discover a minion that costs (5) or more", list);
// Make a copy of the card. This is important to prevent "linking".
minion = await minion.imperfectCopy();
await owner.addToHand(minion);
}
In the 'Drakonid Operative' example i showed above, here's the working code:
This picture is very outdated.