It is currently 03 Sep 2025, 03:23
   
Text Size

spDrawCards

Post MTG Forge Related Programming Questions Here

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

spDrawCards

Postby Rob Cashwalker » 16 Aug 2009, 04:04

This is by far my most involved keyword development ever. It is also the most modular keyword - it has to support a wide variety of slight differences in the "Draw n cards" mechanic.

syntax:
spDrawCards[Tgt]:num:drawback

where num is:
a number.. like 1, 2, 3
Domain
NumCardsType/type/zone
where type is any single card type (artifact, creature, elf)
where zone is:
YouCtrl, InPlay, InYourYard or InAllYards

where drawback is:
Discard/{num | X}[/[AtRandom | UnlessDiscardType/type]]
LoseLife/{num | X}
NumToLibrary/{num | X}/{Top | Bottom | TopOrBottom}
NumOppDraw/{num | X}
where X really means "X" and will take on the number of cards calculated.

If you include "Tgt" to form "spDrawCardsTgt:" then it will request player targeting prior to resolution.

These are all the draw spells already included:
Code: Select all
Brainstorm
U
Instant
Draw three cards, then put two cards from your hand on top of your library in any order.
spDrawCards:3:NumToLibrary/2/Top

Minions' Murmurs
2 B B
Sorcery
You draw X cards and you lose X life, where X is the number of creatures you control.
spDrawCards:NumCardsType/Creature/YouCtrl:LoseLife/X

Harmonize
2 G G
Sorcery
Draw three cards.
spDrawCards:3

Concentrate
2 U U
Sorcery
Draw three cards.
spDrawCards:3

Night's Whisper
1 B
Sorcery
You draw two cards and you lose 2 life.
spDrawCards:2:LoseLife/2

Counsel of the Soratami
2 U
Sorcery
Draw two cards.
spDrawCards:2

Words of Wisdom
1 U
Instant
You draw two cards, then each other player draws a card.
spDrawCards:2:NumOppDraw/1

Ancestral Recall
U
Instant
Target player draws three cards.
spDrawCardsTgt:3
I've run all the complex ones and a couple simple spells in a test deck, under both the human and AI control.

CardFactory:
Code: Select all
    private final int shouldSpDrawCards(Card c){
       ArrayList<String> a = c.getKeyword();
       for (int i = 0; i < a.size(); i++)
          if (a.get(i).toString().startsWith("spDrawCards"))
             return i;
       
       return -1;
    }
Code: Select all
    if (shouldSpDrawCards(card) != -1)
    {
       int n = shouldSpDrawCards(card);
       String parse = card.getKeyword().get(n).toString();
        card.removeIntrinsicKeyword(parse);
       
        String k[] = parse.split(":");
       
        final boolean Tgt[] = {false};
        Tgt[0] = k[0].contains("Tgt");
       
        final int NumCards[] = {-1};
        final String NumCardsType[] = {"none"};
        final boolean NumCardsTypeYouCtrl[] = {false};
        final boolean NumCardsTypeInPlay[] = {false};
        final boolean NumCardsTypeInYourYard[] = {false};
        final boolean NumCardsTypeInAllYards[] = {false};
        final boolean NumCardsDomain[] = {false};
        if (k[1].length() == 1)
           NumCards[0] = Integer.parseInt(k[1]);
        else
        {
           if (k[1].startsWith("NumCardsType"))
           {
              String kk[] = k[1].split("/");
              NumCardsType[0] = kk[1];
              NumCardsTypeYouCtrl[0] = kk[2].equals("YouCtrl");
              NumCardsTypeInPlay[0] = kk[2].equals("InPlay");
              NumCardsTypeInYourYard[0] = kk[2].equals("InYourYard");
              NumCardsTypeInAllYards[0] = kk[2].equals("InAllYards");
           }
           NumCardsDomain[0] = k[1].equals("Domain");
        }
       
        final int NumDiscard[] = {0};
        final String UnlessDiscardType[] = {"none"};
        final boolean AtRandom[] = {false};
       
        final int NumLoseLife[] = {0};
       
        final int NumToLibrary[] = {0};
        final String LibraryPosition[] = {"none"};
       
        final int NumOppDraw[] = {0};
       
        if (k.length > 2)
        {
           if (k[2].contains("Discard"))
           {
              String kk[] = k[2].split("/");
              if (kk[1].equals("X"))
                 NumDiscard[0] = -1;
              else
                 NumDiscard[0] = Integer.parseInt(kk[1]);
              
              if (kk.length > 2)
              {
                 if (kk[2].equals("UnlessDiscardType"))
                    UnlessDiscardType[0] = kk[3];
                 
                 AtRandom[0] = kk[2].equals("AtRandom");
              }
           }

           if (k[2].contains("LoseLife"))
           {
              String kk[] = k[2].split("/");
              if (kk[1].equals("X"))
                 NumLoseLife[0] = -1;
              else
                 NumLoseLife[0] = Integer.parseInt(kk[1]);
           }

           if (k[2].contains("NumToLibrary"))
           {
              String kk[] = k[2].split("/");
              if (kk[1].equals("X"))
                 NumToLibrary[0] = -1;
              else
                 NumToLibrary[0] = Integer.parseInt(kk[1]);
              LibraryPosition[0] = kk[2];
           }
           
           if (k[2].contains("NumOppDraw"))
           {
              String kk[] = k[2].split("/");
              if (kk[1].equals("X"))
                 NumOppDraw[0] = -1;
              else
                 NumOppDraw[0] = Integer.parseInt(kk[1]);
           }
        }
       
        final SpellAbility spDraw = new Spell(card)
        {
           private static final long serialVersionUID = -7049779241008089696L;

           public int getNumCards()
           {
              if (NumCards[0] != -1)
                 return NumCards[0];

              int n = 0;

              String cardController = card.getController();
              PlayerZone myPlay = AllZone.getZone(Constant.Zone.Play, cardController);
              PlayerZone opPlay = AllZone.getZone(Constant.Zone.Play, AllZone.GameAction.getOpponent(cardController));

              PlayerZone myYard = AllZone.getZone(Constant.Zone.Graveyard, cardController);
              PlayerZone opYard = AllZone.getZone(Constant.Zone.Graveyard, AllZone.GameAction.getOpponent(cardController));

              CardList AllCards = new CardList();

              if (! NumCardsType[0].equals("none"))
              {
                 if (NumCardsTypeInYourYard[0] == false)
                    AllCards.addAll(myYard.getCards());

                 if (NumCardsTypeInAllYards[0] == false)
                 {
                    AllCards.addAll(myYard.getCards());
                    AllCards.addAll(opYard.getCards());
                 }

                 if (NumCardsTypeYouCtrl[0] == true)
                    AllCards.addAll(myPlay.getCards());

                 if (NumCardsTypeInPlay[0] == true)
                 {
                    AllCards.addAll(myPlay.getCards());
                    AllCards.addAll(opPlay.getCards());
                 }

                 AllCards = AllCards.filter(new CardListFilter()
                 {
                    public boolean addCard(Card c)
                    {
                       if (c.getType().contains(NumCardsType[0]))
                          return true;

                       return false;
                    }
                 });

                 n = AllCards.size();
              }
              if (NumCardsDomain[0] == true)
              {
                 AllCards.addAll(myPlay.getCards()); 
                 String basic[] = {"Forest", "Plains", "Mountain", "Island", "Swamp"};

                 for(int i = 0; i < basic.length; i++)
                    if (! AllCards.getType(basic[i]).isEmpty())
                       n++;
              }

              if (NumDiscard[0] == -1)
                 NumDiscard[0] = n;
              if (NumLoseLife[0] == -1)
                 NumLoseLife[0] = n;
              if (NumToLibrary[0] == -1)
                 NumToLibrary[0] = n;
              if (NumOppDraw[0] == -1)
                 NumOppDraw[0] = n;

              return n;
           }

           public boolean canPlayAI()
           {
              int n = getNumCards();
              int h = AllZone.getZone(Constant.Zone.Hand, Constant.Player.Computer).size();
              Random r = new Random();

              if (((h + n) - (NumDiscard[0] + NumToLibrary[0]) <= 7)
                    && (AllZone.GameAction.getPlayerLife(Constant.Player.Computer).getLife() - NumLoseLife[0]) >= 8
                    && (r.nextInt(10) > 4))
              {
                 setTargetPlayer(Constant.Player.Computer);
                 return true;
              }

              return false;
           }

           public void resolve()
           {
              int n = getNumCards();

              String TgtPlayer = card.getController();
              if (Tgt[0])
                 TgtPlayer = getTargetPlayer();

              for (int i=0; i < n; i++)
                 AllZone.GameAction.drawCard(TgtPlayer);

              if (NumDiscard[0] > 0)
              {
                 if (!UnlessDiscardType[0].equals("none"))
                    AllZone.GameAction.discardUnless(TgtPlayer, NumDiscard[0], UnlessDiscardType[0]);
                 else if (AtRandom[0] == true)
                    AllZone.GameAction.discardRandom(TgtPlayer, NumDiscard[0]);
                 else
                    AllZone.GameAction.discard(TgtPlayer, NumDiscard[0]);
              }
                 
              if (NumLoseLife[0] > 0)
                 AllZone.GameAction.getPlayerLife(TgtPlayer).subtractLife(NumLoseLife[0]);

              if (NumToLibrary[0] > 0)
                 AllZone.GameAction.handToLibrary(TgtPlayer, NumToLibrary[0], LibraryPosition[0]);

              if (NumOppDraw[0] > 0)
                 for (int i = 0; i < NumOppDraw[0]; i++)
                    AllZone.GameAction.drawCard(AllZone.GameAction.getOpponent(TgtPlayer));
           }
        };

        if (Tgt[0])
           spDraw.setBeforePayMana(CardFactoryUtil.input_targetPlayer(spDraw));

        spDraw.setDescription(card.getText());
        card.setText("");
        card.clearSpellAbility();
        card.addSpellAbility(spDraw);
    }
GameAction.java:
Code: Select all
  public void discardRandom(String player, int numDiscard)
  {
     for (int i=0; i < numDiscard; i++)
     {
        Card[] c = AllZone.getZone(Constant.Zone.Hand, player).getCards();
        if (c.length != 0)
           discard(CardUtil.getRandom(c));
     }
  }

  public void discard(String player, int numDiscard)
  {
     for (int i=0; i < numDiscard; i++)
        if (player.equals(Constant.Player.Human))
           AllZone.InputControl.setInput(CardFactoryUtil.input_discard());
        else
           AI_discard();
  }

  public void discardUnless(String player, int numDiscard, String uType)
  {
     if (player.equals(Constant.Player.Human))
        AllZone.InputControl.setInput(CardFactoryUtil.input_discardNumUnless(numDiscard, uType));
     else
        AI_discardNumUnless(numDiscard, uType);
  }
 
  public void AI_discardNumUnless(int numDiscard, String uType)
  {
     CardList hand = new CardList();
     hand.addAll(AllZone.getZone(Constant.Zone.Hand, Constant.Player.Computer).getCards());
     CardList tHand = hand.getType(uType);
    
     if (tHand.size() > 0)
     {
        CardListUtil.sortCMC(tHand);
        tHand.reverse();
        discard(tHand.get(0));
        return;
     }
     for (int i=0; i < numDiscard; i++)
        AI_discard();
  }
 
  public void AI_discard()
  {
     CardList hand = new CardList();
     hand.addAll(AllZone.getZone(Constant.Zone.Hand, Constant.Player.Computer).getCards());
    
     CardList blIP = new CardList();
     blIP.addAll(AllZone.getZone(Constant.Zone.Play, Constant.Player.Computer).getCards());
     blIP = blIP.getType("Basic");
     if (blIP.size() > 5)
     {
        CardList blIH = hand.getType("Basic");
        if (blIH.size() > 0)
        {
           discard(blIH.get(CardUtil.getRandomIndex(blIH)));
           return;
        }
       
        CardListUtil.sortAttackLowFirst(hand);
        CardListUtil.sortNonFlyingFirst(hand);
        discard(hand.get(0));
        return;
     }
     else
     {
        CardListUtil.sortCMC(hand);
        discard(hand.get(0));
        return;
     }
  }

  public void handToLibrary(String player, int numToLibrary, String libPos)
  {    
     if (player.equals(Constant.Player.Human))
     {
        if (libPos.equals("Top") || libPos.equals("Bottom"))
           libPos = libPos.toLowerCase();
        else
        {
           Object o = new Object();
           String s = "card";
           if (numToLibrary > 1)
              s = "cards";

           o = AllZone.Display.getChoice("Do you want to put the " + s + " on the top or bottom of your library?", new Object[] {"top", "bottom"});
           libPos = o.toString();
        }
        AllZone.InputControl.setInput(CardFactoryUtil.input_putFromHandToLibrary(libPos, numToLibrary));
     }
     else
     {
        for (int i=0; i < numToLibrary; i++)
        {
           if (libPos.equals("Top") || libPos.equals("Bottom"))
              libPos = libPos.toLowerCase();
           else
           {
              Random r = new Random();
              if (r.nextBoolean())
                 libPos = "top";
              else
                 libPos = "bottom";
           }
           AI_handToLibrary(libPos);
        }
     }
  }
 
  public void AI_handToLibrary(String libPos)
  {
     CardList hand = new CardList();
     hand.addAll(AllZone.getZone(Constant.Zone.Hand, Constant.Player.Computer).getCards());
    
     CardList blIP = new CardList();
     blIP.addAll(AllZone.getZone(Constant.Zone.Play, Constant.Player.Computer).getCards());
     blIP = blIP.getType("Basic");
     if (blIP.size() > 5)
     {
        CardList blIH = hand.getType("Basic");
        if (blIH.size() > 0)
        {
           Card card = blIH.get(CardUtil.getRandomIndex(blIH));
           AllZone.Computer_Hand.remove(card);
           if (libPos.equals("top"))
              AllZone.Computer_Library.add(card, 0);
           else
              AllZone.Computer_Library.add(card);
          
           return;
        }
       
        CardListUtil.sortAttackLowFirst(hand);
        CardListUtil.sortNonFlyingFirst(hand);
        discard(hand.get(0));
        return;
     }
     else
     {
        CardListUtil.sortCMC(hand);
        discard(hand.get(0));
        return;
     }    
  }
CardFactoryUtil.java:
Code: Select all
  public static Input input_putFromHandToLibrary(final String TopOrBottom, final int num)
  {
     Input target = new Input()
     { 
      private static final long serialVersionUID = 5178077952030689103L;
      public int n = 0;
      
      public void showMessage()
        {
           AllZone.Display.showMessage("Select a card to put on the " + TopOrBottom + " of your library.");
           ButtonUtil.disableAll();
          
           if (n == num || AllZone.Human_Hand.getCards().length == 0)
              stop();
        }
        public void selectButtonCancel() {stop();}
        public void selectCard(Card card, PlayerZone zone)
        {
           if (zone.is(Constant.Zone.Hand))
           {
              AllZone.Human_Hand.remove(card);
             
              if (TopOrBottom.equals("top"))
                 AllZone.Human_Library.add(card, 0);
              else if (TopOrBottom.equals("bottom"))
                 AllZone.Human_Library.add(card);
             
              n++;
              if (n == num)
                 stop();
             
              showMessage();
           }
        }
     };
     return target;
  }
 
  public static Input input_discardNumUnless(final int nCards, final String uType)
  {
     Input target = new Input()
     {
      private static final long serialVersionUID = 8822292413831640944L;
      
      int n = 0;
        public void showMessage()
        {
           AllZone.Display.showMessage("Select " + (nCards - n) + " cards to discard, unless you discard a " + uType + ".");
           ButtonUtil.disableAll();
          
           if (n == nCards || AllZone.Human_Hand.getCards().length == 0)
              stop();
        }
        public void selectButtonCancel() {stop();}
        public void selectCard(Card card, PlayerZone zone)
        {
           if (zone.is(Constant.Zone.Hand))
           {
              AllZone.GameAction.discard(card);
              n++;
             
              if (card.getType().contains(uType))
                 stop();
             
              showMessage();
           }
        }
     };
    
     return target;
  }
It's important to note that I wrote an AI for discarding cards that can be applied throughout, where the AI currently discards randomly. The Hand to Library code uses the same logic for now.... I think there should be a difference in logic, but I just wanted to get this done, and it seemed as good as any.

The hand to library "TopOrBottom" function puts BOTH cards either on the top or the bottom. The usual wording of the general mechanic allows the top or bottom decision for each card. But the version of it that's used on a draw spell does specify BOTH. So until the basic code is hacked for the other form, to be used in other spells (scry anyone?) just be aware that it asks once for top or bottom, and then the second (or third, or last) card you select will be the very top card, followed by the prior cards.

The spells are playable by the AI. It will only play them to refill the hand as long as after the discard drawback, its hand will be less than 7. So if it has 5 cards, it won't play Ancestral Recall, but it would play Brainstorm or Sift, when it's added.

Also, I chose 8 as the cut-off for losing life drawbacks. I don't recall any gain life (self or opponent) "drawbacks" on drawspells, but I could easily add it just in case it does show up at some point, or to help support possible fan-made cards.

For targeted spells, the AI will always choose itself... unless and until milling is a viable strategy. (hint hint add the check in SBE, it's so easy)
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

Re: spDrawCards

Postby Marek14 » 16 Aug 2009, 06:17

Rob Cashwalker wrote:Also, I chose 8 as the cut-off for losing life drawbacks. I don't recall any gain life (self or opponent) "drawbacks" on drawspells, but I could easily add it just in case it does show up at some point, or to help support possible fan-made cards.
Kiss of the Amesha
Bargain
Armistice

Other effects which do something in addition to draw:
Arcane Spyglass
Fa'adiyah Seer and Sindbad
Fungal Shambler
Marek14
Tester
 
Posts: 2773
Joined: 07 Jun 2008, 07:54
Has thanked: 0 time
Been thanked: 303 times

Re: spDrawCards

Postby Rob Cashwalker » 16 Aug 2009, 14:45

Marek14 wrote:Kiss of the Amesha
Bargain
Armistice

Other effects which do something in addition to draw:
Arcane Spyglass
Fa'adiyah Seer and Sindbad
Fungal Shambler
I'd rather do those with dedicated life gain spells, since the primary AI on them would be related to gaining life, not drawing cards. BTW, if you specify any negative number other than -1 (used in handling "X") you could effectively make the gain life work as is....
And this keyword doesn't cover permanent abilities.
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

Re: spDrawCards

Postby DennisBergkamp » 16 Aug 2009, 17:02

Wow, as always, great stuff Rob! This one seems to cover a lot... and must've taken a long time to implement.
I'll merge this asap, and release a new version in the next few days.
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: spDrawCards

Postby DennisBergkamp » 16 Aug 2009, 20:14

One problem with merging: you created a function sortCMC in CardListUtil:

Code: Select all
CardListUtil.sortCMC(tHand);
Could you post the changes you made in CardListUtil as well :) ?
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: spDrawCards

Postby Rob Cashwalker » 17 Aug 2009, 11:31

Code: Select all
  @SuppressWarnings("unchecked") // Comparator needs <type>
public static void sortCMC(CardList list)
{
   Comparator com = new Comparator()
   {
      public int compare(Object a1, Object b1)
      {
         Card a = (Card)a1;
         Card b = (Card)b1;
         
         int cmcA = CardUtil.getConvertedManaCost(a.getManaCost());
         int cmcB = CardUtil.getConvertedManaCost(b.getManaCost());
         
         if (cmcA == cmcB)
            return 0;
         if (cmcA > cmcB)
            return -1;
         if (cmcB > cmcA)
            return 1;
            
         return 0;
      }
   };
   list.sort(com);
}
I didn't analyze the computer's AI on discard, so I wasn't positive that this sort worked in the correct order (lowest to highest).
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

Re: spDrawCards

Postby mtgrares » 17 Aug 2009, 15:57

Great work Rob, hopefully you didn't pull out any hair. :D
mtgrares
DEVELOPER
 
Posts: 1352
Joined: 08 Sep 2008, 22:10
Has thanked: 3 times
Been thanked: 12 times

Re: spDrawCards

Postby Rob Cashwalker » 17 Aug 2009, 17:45

No.. no hair loss that I'm aware of. But I did lose a little sleep.

Funny enough, while I complain about my wife's facebook habits in marriage counseling, she's equally complained of my coding habits for MTGForge. While she has virtual farms and virtual mafias to her credit, I've gotten hundreds of cards added to the game.
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

Re: spDrawCards

Postby Chris H. » 19 Aug 2009, 12:51

I have been fairly busy in recent days. I'm sure that Dennis would like to release the next version in the near future. Rob has done a wonderful job with this keyword, and I have to agree that the syntax is complicated.

I did a search on the word "Draw" and was surprised to see so many hits. I grabbed eight cards that can be implemented for the next version. These cards are fairly simple in syntax. There are still many candidates that are not yet implemented/submitted. Some of these will require a more complicated form of the syntax.

The cards that I am submitting are:

Code: Select all
Ambition's Cost
3 B
Sorcery
You draw three cards and you lose 3 life.
spDrawCards:3:LoseLife/3

Ancient Craving
3 B
Sorcery
You draw three cards and you lose 3 life.
spDrawCards:3:LoseLife/3

Brilliant Plan
4 U
Sorcery
Draw three cards.
spDrawCards:3

Careful Study
U
Sorcery
Draw two cards, then discard two cards.
spDrawCards:2:Discard/2

Catalog
2 U
Instant
Draw two cards, then discard a card.
spDrawCards:2:Discard/1

Control of the Court
1 R
Sorcery
Draw four cards, then discard three cards at random.
spDrawCards:4:Discard/3/AtRandom

Divination
2 U
Sorcery
Draw two cards.
spDrawCards:2

Goblin Lore
1 R
Sorcery
Draw four cards, then discard three cards at random.
spDrawCards:4:Discard/3/AtRandom
`
@ Dennis:

I will pm the card data and the jpg urls to you.
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: spDrawCards

Postby Rob Cashwalker » 19 Aug 2009, 14:39

Those look good. Yeah, there are a lot of hits if you're just searching for "Draw"....

When I was doing the research for the different forms, I found it best to filter the phrase "Draw a card", since it's a tack-on effect, and present in "cycling", another tack-on. I didn't think of it at the time, but also filter "whenever" or "next", as there were some spells that created triggers. Also limit the search to just instants and sorceries.

If you come across any additional variations that seem simple, logical or popular enough, then let me know and I'll add it.
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

Re: spDrawCards

Postby Chris H. » 19 Aug 2009, 14:59

Once I filtered out the cantrip and cycling cards the list became a wee bit easier to handle. There was still a third type which states:

Draw a card at the beginning of the next turn's upkeep.

This one looks like it should be a separate keyword and might be somewhat similar to the cantrips.
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: spDrawCards

Postby Rob Cashwalker » 20 Aug 2009, 02:33

Yeah.. those were the ones I was talking about... I'm not about to try to figure out a delayed trigger of that magnitude....
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

Re: spDrawCards

Postby mtgrares » 21 Aug 2009, 17:30

Rob Cashwalker wrote:No.. no hair loss that I'm aware of. But I did lose a little sleep.

Funny enough, while I complain about my wife's facebook habits in marriage counseling, she's equally complained of my coding habits for MTGForge. While she has virtual farms and virtual mafias to her credit, I've gotten hundreds of cards added to the game.
Don't get me started on therapists.
mtgrares
DEVELOPER
 
Posts: 1352
Joined: 08 Sep 2008, 22:10
Has thanked: 3 times
Been thanked: 12 times

Re: spDrawCards

Postby Chris H. » 22 Aug 2009, 14:34

Chris H. wrote:Rob has done a wonderful job with this keyword, and I have to agree that the syntax is complicated.

There are still many candidates that are not yet implemented/submitted. Some of these will require a more complicated form of the syntax.
I think that I have figured out how to add an additional 15 cards. Many of these 15 cards require a fairly complicated form of the syntax, but I think that I have the following cards figured out. :)

I may have missed a number of cards that can be included, if anyone else figures out a few more of these cards, that would be great. 8)

The cards that I am submitting are:

Code: Select all
Airborne Aid
3 U
Sorcery
Draw a card for each Bird on the battlefield.
spDrawCards:NumCardsType/Bird/InPlay

Collective Unconscious
4 G G
Sorcery
Draw a card for each creature you control.
spDrawCards:NumCardsType/Creature/YouCtrl

Compulsive Research
2 U
Sorcery
Target player draws three cards. Then that player discards two cards unless he or she discards a land card.
spDrawCardsTgt:3:Discard/2/UnlessDiscardType/Land

Dream Cache
2 U
Sorcery
Draw three cards, then put two cards from your hand both on top of your library or both on the bottom of your library.
spDrawCards:3:NumToLibrary/2/TopOrBottom

Flow of Ideas
5 U
Sorcery
Draw a card for each Island you control.
spDrawCards:NumCardsType/Island/YouCtrl

Inspiration
3 U
Instant
Target player draws two cards.
spDrawCardsTgt:2

Opportunity
4 U U
Instant
Target player draws four cards.
spDrawCardsTgt:4

Ribbons of the Reikai
4 U
Sorcery Arcane
Draw a card for each Spirit you control.
spDrawCards:NumCardsType/Spirit/YouCtrl

Sift
3 U
Sorcery
Draw three cards, then discard a card.
spDrawCards:3:Discard/1

Sign in Blood
B B
Sorcery
Target player draws two cards and loses 2 life.
spDrawCardsTgt:2:LoseLife/2

Thirst for Knowledge
2 U
Instant
Draw three cards. Then discard two cards unless you discard an artifact card.
spDrawCards:3:Discard/2/UnlessDiscardType/Artifact

Tidings
3 U U
Sorcery
Draw four cards.
spDrawCards:4

Touch of Brilliance
3 U
Sorcery
Draw two cards.
spDrawCards:2

Vision Skeins
1 U
Instant
Each player draws two cards.
spDrawCards:2:NumOppDraw/2

Wistful Thinking
2 U
Sorcery
Target player draws two cards, then discards four cards.
spDrawCardsTgt:2:Discard/4
`
@ Dennis:

I will pm the card data and the jpg urls to you.
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: spDrawCards

Postby Huggybaby » 22 Aug 2009, 15:25

Rob Cashwalker wrote:While she has virtual farms and virtual mafias to her credit, I've gotten hundreds of cards added to the game.
My fiancee just got started on Facebook, and she has a farm now too. It's quite pointless IMO but so are my games I suppose. I think her time is better spent cooking me food and rubbing my feet.
User avatar
Huggybaby
Administrator
 
Posts: 3227
Joined: 15 Jan 2006, 19:44
Location: Finally out of Atlanta
Has thanked: 749 times
Been thanked: 601 times

Next

Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 29 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 29 users online :: 0 registered, 0 hidden and 29 guests (based on users active over the past 10 minutes)
Most users ever online was 7303 on 15 Jul 2025, 20:46

Users browsing this forum: No registered users and 29 guests

Login Form