Every Magic: The Gathering card in XMage is a self-contained Java class that extendsDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/magefree/mage/llms.txt
Use this file to discover all available pages before exploring further.
CardImpl and lives inside the Mage.Sets module. The class declares the card’s types, mana cost, power/toughness, subtypes, and abilities in its constructor, then exposes a copy() method so the game engine can safely clone the card for each player who owns it. Before writing any code by hand, XMage ships a Perl code-generation utility that builds a skeleton from the official card database, saving you from repetitive boilerplate.
Directory and Naming Conventions
All card classes live underMage.Sets/src/mage/cards/. Cards are grouped into a subdirectory named after the lowercase first letter of the card’s name:
AangAirNomad and lives in mage/cards/a/AangAirNomad.java.
Classes are declared public final — no further subclassing is expected — and the package matches the directory:
Generating a Skeleton with gen-card.pl
The Utils/ directory contains gen-card.pl, a Perl script that reads the bundled card database (mtg-cards-data.txt) and renders a ready-to-compile skeleton via a Text::Template template (cardClass.tmpl). Run it from the Utils/ directory:
.java file into the correct Mage.Sets subdirectory. It also generates a matching test skeleton via gen-card-test.pl if you need a starting point for your JUnit tests.
gen-card.pl requires mtg-cards-data.txt and mtg-sets-data.txt in the same Utils/ directory. These files are bundled in the repository. If the card name is not found, the script will suggest close matches.Anatomy of a Card Class
Every card class follows the same four-part structure:- Public constructor
(UUID ownerId, CardSetInfo setInfo)— declares everything about the card. - Private copy constructor
(final YourCard card)— delegates tosuper(card). copy()override — returnsnew YourCard(this).- Inner classes (optional) — custom ability or effect classes defined in the same file when no reusable common class exists.
Constructor Parameters
The first call inside the constructor must besuper(...) with four arguments:
| Argument | Type | Description |
|---|---|---|
ownerId | UUID | The player who owns this card instance (passed through) |
setInfo | CardSetInfo | Set code, card number, rarity, and art info |
cardTypes | CardType[] | One or more from CardType.CREATURE, INSTANT, SORCERY, ARTIFACT, ENCHANTMENT, LAND, PLANESWALKER, KINDRED, BATTLE |
costs | String | Mana cost string, e.g. "{1}{W}{W}", "{R}", "{0}" |
Declaring Card Attributes
After thesuper() call, set card-specific attributes directly on instance fields:
Adding Abilities
Callthis.addAbility(...) once per ability. XMage provides hundreds of pre-built ability classes in mage.abilities.common and keyword abilities in mage.abilities.keyword. Always check these packages before writing a new ability from scratch:
SpellAbility rather than a new ability instance:
Complete Examples
Registering the Card in a Set
A card class that is not registered in a set file will never appear in the game. Each set has a corresponding class inMage.Sets/src/mage/sets/ that extends ExpansionSet and registers cards via cards.add(new SetCardInfo(...)):
SetCardInfo entry to each relevant set file. The NON_FULL_USE_VARIOUS constant is used when a single class is shared across multiple printings with different art.
Full Implementation Flow
The generated file is placed in the correct
Mage.Sets/src/mage/cards/<letter>/ subdirectory automatically.Open the generated file and verify the
super() call has the right CardType array and mana cost string. Add supertype, subtype, power, and toughness assignments as needed.Search
mage.abilities.common and mage.abilities.keyword for an existing class that matches the card text. Call this.addAbility(...) for each ability. For spells, use this.getSpellAbility().addEffect(...) and this.getSpellAbility().addTarget(...).If no matching ability exists in
mage.abilities.common, write inner classes extending TriggeredAbilityImpl, ActivatedAbilityImpl, or StaticAbility, and custom effects extending OneShotEffect or ContinuousEffectImpl. See the Abilities & Effects reference for details.Open the matching class in
Mage.Sets/src/mage/sets/ and add a SetCardInfo entry with the card’s name, collector number, rarity, and class reference.Build and run the test suite to verify there are no compilation errors and the card behaves correctly:
The copy() Method
Every card class must implement copy(). The game engine copies cards constantly — when they are put into play, when effects duplicate them, and when game state is cloned for AI simulation. The copy constructor pattern delegates all field copying to super(card):
