It is currently 29 Oct 2025, 10:30
   
Text Size

How to implement new cards

Post MTG Forge Related Programming Questions Here

Moderators: timmermac, Agetian, friarsol, Blacksmith, KrazyTheFox, CCGHQ Admins

How to implement new cards

Postby fissionessence » 22 Apr 2011, 04:52

I only recently started playing with Forge, and even more recently noticed the openness of the card scripting. I read up on how to script a card, and made one of my own, but I don't know how to actually implement it in the program. I read all of the documentation I could find that I thought might answer this, but they all talk about how to script a card, and not so much how to actually make it fully implemented.

Is it possible for me to write and implement my own cards on my computer, or do all cards have to be written into the program? Do I need to use the java to modify the program? The cardsfolder kind of made it just look like I could throw a new .txt in there and it would work . . . but it didn't show up in the deck editor.

I'm not looking to program in existing Magic cards that aren't yet in Forge; I want to insert my own custom cards, so this is for personal use.

Sorry if the answer is obvious. If you could just direct me to where I can read up on this, I'd be happy to do so :)
fissionessence
 
Posts: 4
Joined: 22 Apr 2011, 04:42
Has thanked: 0 time
Been thanked: 0 time

Re: How to implement new cards

Postby Hellfish » 22 Apr 2011, 07:23

Hello and welcome! :)

Indeed, you should be able to put a correctly formatted text file in cardsfolder and have Forge pick up on it. There may be some errors in it that are hard to spot if you havn't scripted a lot so if you could post the card script we could take a look.
The wiki is probably the best place for info, along with various forum topics and asking people, but I believe you knew that. :)
So now you're
Screaming for the blood of the cookie monster
Evil puppet demon of obesity
Time to change the tune of his fearful ballad
C is for "Lettuce," that's good enough for me
User avatar
Hellfish
Programmer
 
Posts: 1297
Joined: 07 Jun 2009, 10:41
Location: South of the Pumphouse
Has thanked: 110 times
Been thanked: 169 times

Re: How to implement new cards

Postby fissionessence » 22 Apr 2011, 15:34

Thanks for replying! Here is my card:

Code: Select all
Form of the Kraken
ManaCost:5 U U U
Types:Enchantment
Text:no text
T:Mode$ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, put eight 3/3 blue Tentacle creature tokens with reach onto the battlefield.
SVar:TrigToken:AB$Token | Cost$ 0 | TokenAmount$ 8 | TokenName$ Tentacle | TokenTypes$ Creature,Tentacle | TokenOwner$ You | TokenColors$ Blue | TokenPower $ 3 | TokenToughness$ 3 | TokenKeywords$ Reach
T:Mode$ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigSac | TriggerDescription$ When CARDNAME leaves the battlefield, sacrifice all Tentacles you control.
SVar:TrigSac:AB$DestroyAll | Cost$ 0 | ValidCards$ Tentacle
SVar:Rarity:Mythic Rare
End
At first, when I tried to load the program it gave me an error. After a couple iterations of fixes, the program loads fine with this version. The problem I'm having is that when I go into the deck editor, the card doesn't show up :(

~
fissionessence
 
Posts: 4
Joined: 22 Apr 2011, 04:42
Has thanked: 0 time
Been thanked: 0 time

Re: How to implement new cards

Postby friarsol » 22 Apr 2011, 15:55

Take a look at one of the other cards in cardsfolder/ you are missing the first parameter of the very first line

Name:Form of the Kraken
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: How to implement new cards

Postby Chris H. » 22 Apr 2011, 15:59

fissionessence wrote:Thanks for replying! Here is my card:

Code: Select all
Form of the Kraken
ManaCost:5 U U U
Types:Enchantment
Text:no text
T:Mode$ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, put eight 3/3 blue Tentacle creature tokens with reach onto the battlefield.
SVar:TrigToken:AB$Token | Cost$ 0 | TokenAmount$ 8 | TokenName$ Tentacle | TokenTypes$ Creature,Tentacle | TokenOwner$ You | TokenColors$ Blue | TokenPower $ 3 | TokenToughness$ 3 | TokenKeywords$ Reach
T:Mode$ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigSac | TriggerDescription$ When CARDNAME leaves the battlefield, sacrifice all Tentacles you control.
SVar:TrigSac:AB$DestroyAll | Cost$ 0 | ValidCards$ Tentacle
SVar:Rarity:Mythic Rare
End
At first, when I tried to load the program it gave me an error. After a couple iterations of fixes, the program loads fine with this version. The problem I'm having is that when I go into the deck editor, the card doesn't show up :(

~
`
Try adding the card name label before the cards name. The first line of you card should look like this:

Code: Select all
Name:Form of the Kraken
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: How to implement new cards

Postby fissionessence » 22 Apr 2011, 18:30

What an embarrassing brain fart :) Thanks, guys!

Now, will that card automatically appear in quest reward packs and the shop, or is there more stuff I need to make that happen?
fissionessence
 
Posts: 4
Joined: 22 Apr 2011, 04:42
Has thanked: 0 time
Been thanked: 0 time

Re: How to implement new cards

Postby friarsol » 22 Apr 2011, 19:20

fissionessence wrote:What an embarrassing brain fart :) Thanks, guys!

Now, will that card automatically appear in quest reward packs and the shop, or is there more stuff I need to make that happen?
It's pretty plug and play, although with how many cards we have the probability of actually getting 1 of your cards is... slim. Just be careful with custom cards, if you move to the next version you need to bring your custom cards forward, otherwise you may have decks that will be be unusable without them.
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

Re: How to implement new cards

Postby Chris H. » 22 Apr 2011, 19:21

fissionessence wrote:Now, will that card automatically appear in quest reward packs and the shop, or is there more stuff I need to make that happen?
`
I believe that as long as the card has a rarity value in the card file that it will be included as a possible card that could appear in a pack, card shop or as a special reward if the card is a rare.
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: How to implement new cards

Postby lazylockie » 22 Apr 2011, 21:20

Chris H. wrote:
fissionessence wrote:Now, will that card automatically appear in quest reward packs and the shop, or is there more stuff I need to make that happen?
`
I believe that as long as the card has a rarity value in the card file that it will be included as a possible card that could appear in a pack, card shop or as a special reward if the card is a rare.
I think this used to work, but doesn't anymore. I used to play with a Standard only pool this way, but since revision 7000 (I guess) putting a # on SetInfo doesn't work anymore :(
lazylockie
 
Posts: 508
Joined: 13 Jul 2010, 22:44
Has thanked: 74 times
Been thanked: 15 times

Re: How to implement new cards

Postby Chris H. » 22 Apr 2011, 23:01

lazylockie wrote:I think this used to work, but doesn't anymore. I used to play with a Standard only pool this way, but since revision 7000 (I guess) putting a # on SetInfo doesn't work anymore :(
`
The recent work on sets is an ongoing project. I think that everyone including the dev will be happy to see this finished. 8)
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: How to implement new cards

Postby fissionessence » 23 Apr 2011, 00:34

I kind of got lost in the back-and-forth here . . . so the cards will not appear in the quest packs, shop, and rewards?

Also, I have a couple other questions:

Why do some of the card files I look at have just card text, but no 'rules programming'? But they still work fine in the game. Jace, the Mind Sculptor doesn't even have any text in the file, but an AI deck was using him!

Also, for the cards I put in, I also put images in the pics folder. They show up in the deck editor, but when I play with them, they're black, as if there were no picture :( How can I fix this?
fissionessence
 
Posts: 4
Joined: 22 Apr 2011, 04:42
Has thanked: 0 time
Been thanked: 0 time

Re: How to implement new cards

Postby Chris H. » 23 Apr 2011, 01:24

fissionessence wrote:I kind of got lost in the back-and-forth here . . . so the cards will not appear in the quest packs, shop, and rewards?
`
Some recent code additions are adding set info to each and every card file. This part of the project is ongoing and is not yet finished. This might explain a few of the noticed oddities. :D

Why do some of the card files I look at have just card text, but no 'rules programming'? But they still work fine in the game. Jace, the Mind Sculptor doesn't even have any text in the file, but an AI deck was using him!
`
Some cards are scripted. The lines starting with a K, T or A is script. A portion of the code will analyze the script and this script tells the computer how to handle the card in game terms.

Some cards are hard coded. The code for Jace, the Mind Sculptor looks like this:

Code: Select all
        //*************** START *********** START **************************
        else if (cardName.equals("Jace, the Mind Sculptor")) {
            final int turn[] = new int[1];
            turn[0] = -1;
             
            final Ability ability1 = new Ability(card, "0") {
                @Override
                public void resolve() {
                    turn[0] = AllZone.Phase.getTurn();
                    card.addCounterFromNonEffect(Counters.LOYALTY, 2);
                    Player targetPlayer = getTargetPlayer();
                   
                    PlayerZone lib = AllZone.getZone(Constant.Zone.Library, targetPlayer);
                   
                    if (lib.size() == 0) return;
                   
                    Card c = lib.get(0);
                   
                    if (card.getController().equals(AllZone.HumanPlayer)) {
                        StringBuilder question = new StringBuilder();
                        question.append("Put the card ").append(c).append(" on the bottom of the ");
                        question.append(c.getController()).append("'s library?");
                       
                        if (GameActionUtil.showYesNoDialog(card, question.toString())) {
                            AllZone.GameAction.moveToBottomOfLibrary(c);
                        }
                       
                    } else //compy
                    {
                        PlayerZone humanPlay = AllZone.getZone(Constant.Zone.Battlefield, AllZone.HumanPlayer);
                        CardList land = new CardList(humanPlay.getCards());
                        land = land.getType("Land");
                       
                        //TODO: improve this:
                        if(land.size() > 4 && c.isLand()) ;
                        else {
                           AllZone.GameAction.moveToBottomOfLibrary(c);
                        }
                    }
                }
               
                @Override
                public boolean canPlayAI() {
                    return card.getCounters(Counters.LOYALTY) < 13 && AllZone.Human_Library.size() > 2;
                }
               
                @Override
                public boolean canPlay() {
                    return AllZone.getZone(card).is(Constant.Zone.Battlefield)
                            && turn[0] != AllZone.Phase.getTurn()
                            && Phase.canCastSorcery(card.getController());
                }//canPlay()
            };
            ability1.setDescription("+2: Look at the top card of target player's library. You may put that card on the bottom of that player's library.");
            StringBuilder stack1 = new StringBuilder();
            stack1.append(card.getName()).append(" - Look at the top card of target player's library. You may put that card on the bottom of that player's library.");
            ability1.setStackDescription(stack1.toString());
           
            ability1.setBeforePayMana(CardFactoryUtil.input_targetPlayer(ability1));
            ability1.setChooseTargetAI(CardFactoryUtil.AI_targetHuman());
            card.addSpellAbility(ability1);
           
            final Ability ability2 = new Ability(card, "0") {
                @Override
                public void resolve() {
                    turn[0] = AllZone.Phase.getTurn();
                    card.getController().drawCards(3);
                   
                    Player player = card.getController();
                    if(player.equals(AllZone.HumanPlayer)) humanResolve();
                    //else
                    //  computerResolve();
                }
               
                public void humanResolve() {
                    PlayerZone hand = AllZone.getZone(Constant.Zone.Hand, AllZone.HumanPlayer);
                   
                    CardList putOnTop = new CardList(hand.getCards());
                   
                    if(putOnTop.size() > 0) {
                    Object o = GuiUtils.getChoice("First card to put on top: ", putOnTop.toArray());
                       if(o != null) {
                           Card c1 = (Card) o;
                           putOnTop.remove(c1);
                           AllZone.GameAction.moveToLibrary(c1);
                       }
                    }
                   
                    putOnTop = new CardList(hand.getCards());
                   
                    if(putOnTop.size() > 0) {
                       Object o = GuiUtils.getChoice("Second card to put on top: ", putOnTop.toArray());
                       if(o != null) {
                           Card c2 = (Card) o;
                           putOnTop.remove(c2);
                           AllZone.GameAction.moveToLibrary(c2);
                       }
                    }
                }
               
                @Override
                public boolean canPlayAI() {
                    return false;
                }
               
                @Override
                public boolean canPlay() {
                    return AllZone.getZone(card).is(Constant.Zone.Battlefield)
                            && turn[0] != AllZone.Phase.getTurn()
                            && Phase.canCastSorcery(card.getController());
                }//canPlay()
            };
            ability2.setDescription("0: Draw three cards, then put two cards from your hand on top of your library in any order.");
            StringBuilder stack2 = new StringBuilder();
            stack2.append(card.getName()).append(" - Draw three cards, then put two cards from your hand on top of your library in any order.");
            ability2.setStackDescription(stack2.toString());
            card.addSpellAbility(ability2);
           
            final Ability ability3 = new Ability(card, "0") {
                @Override
                public void resolve() {
                    turn[0] = AllZone.Phase.getTurn();
                    card.subtractCounter(Counters.LOYALTY, 1);
                   
                    if(AllZone.GameAction.isCardInPlay(getTargetCard())
                            && CardFactoryUtil.canTarget(card, getTargetCard())) {
                            AllZone.GameAction.moveToHand(getTargetCard());
                    }//if
                }//resolve()
               
                @Override
                public boolean canPlay() {
                    return card.getCounters(Counters.LOYALTY) >= 1
                            && AllZone.getZone(card).is(Constant.Zone.Battlefield)
                            && turn[0] != AllZone.Phase.getTurn()
                            && Phase.canCastSorcery(card.getController());
                }
            };
            ability3.setDescription("-1: Return target creature to its owner's hand.");
            StringBuilder stack3 = new StringBuilder();
            stack3.append(card.getName()).append(" - Return target creature to its owner's hand.");
            ability3.setStackDescription(stack3.toString());
           
            ability3.setBeforePayMana(CardFactoryUtil.input_targetCreature(ability3));
            card.addSpellAbility(ability3);
           
            final Ability ability4 = new Ability(card, "0") {
                @Override
                public void resolve() {
                    turn[0] = AllZone.Phase.getTurn();
                    card.subtractCounter(Counters.LOYALTY, 12);
                   
                    Player player = getTargetPlayer();
                   
                    PlayerZone lib = AllZone.getZone(Constant.Zone.Library, player);
                    PlayerZone hand = AllZone.getZone(Constant.Zone.Hand, player);
                   
                    CardList libList = new CardList(lib.getCards());
                    CardList handList = new CardList(hand.getCards());
                   
                    for(Card c:libList)
                        AllZone.GameAction.exile(c);
                   
                    for(Card c:handList) {
                        AllZone.GameAction.moveToLibrary(c);
                    }
                    player.shuffle();
                }
               
                @Override
                public boolean canPlayAI() {
                    int libSize = AllZone.Human_Library.size();
                    int handSize = AllZone.Human_Hand.size();
                    return libSize > 10 && (libSize > handSize);
                }
               
                @Override
                public boolean canPlay() {
                    return card.getCounters(Counters.LOYALTY) >= 12
                            && AllZone.getZone(card).is(Constant.Zone.Battlefield)
                            && turn[0] != AllZone.Phase.getTurn()
                            && Phase.canCastSorcery(card.getController());
                }
            };
            ability4.setDescription("-12: Exile all cards from target player's library, then that player shuffles his or her hand into his or her library.");
            StringBuilder stack4 = new StringBuilder();
            stack4.append(card.getName()).append(" - Exile all cards from target player's library, then that player shuffles his or her hand into his or her library.");
            ability4.setStackDescription(stack4.toString());
           
            ability4.setBeforePayMana(CardFactoryUtil.input_targetPlayer(ability4));
            ability4.setChooseTargetAI(CardFactoryUtil.AI_targetHuman());
            card.addSpellAbility(ability4);
           
            card.setSVars(card.getSVars());
            card.setSets(card.getSets());
           
            return card;
        }//*************** END ************ END **************************
`
Some cards are a combination of both code and script.

Also, for the cards I put in, I also put images in the pics folder. They show up in the deck editor, but when I play with them, they're black, as if there were no picture :( How can I fix this?
`
Rob might be able to answer this one as he is closer to this portion of the code base.
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: How to implement new cards

Postby Rob Cashwalker » 25 Apr 2011, 17:16

Also, for the cards I put in, I also put images in the pics folder. They show up in the deck editor, but when I play with them, they're black, as if there were no picture How can I fix this?
Probably just the lack of SetInfo. Since it's not a real card, there is no set code listed in the SetInfo.java file. I guess you could give it the "Media Insert Promo" set code "MBP".

So add this line in there:
Code: Select all
SetInfo:MBP:Mythic:http://your.own.url/your_image/path.jpg
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times


Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 9 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 9 users online :: 0 registered, 0 hidden and 9 guests (based on users active over the past 10 minutes)
Most users ever online was 9298 on 10 Oct 2025, 12:54

Users browsing this forum: No registered users and 9 guests

Login Form