It is currently 18 May 2024, 05:43
   
Text Size

spAllPump keyword

Post MTG Forge Related Programming Questions Here

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

spAllPump keyword

Postby Rob Cashwalker » 05 Jun 2010, 01:33

My first code submission was a 420 line block that handled a couple dozen multi-creature pump spells. (I wanted to use Overrun and at the time, the closest Forge had was Tromp the Domains) It was a huge assortment of "if's", that provided for all the various spells.

I have now built a keyword handler to perform the same function, plus enables additional spells. (even Titanic Ultimatum can be converted to this keyword)

Basic Syntax:
Code: Select all
spAllPump:{All | YouCtrl [/Restrictions]}:p/t/kw's:[Drawback]:spDesc:stDesc
Restrictions follow the same convention as spDestroyTgt. So, "Black creatures you control get +1/+1" would be spAllPump:YouCtrl/Black:+1/+1.

CardFactory:
Code: Select all
        if (hasKeyword(card, "spAllPump") != -1)
        {
           int n = hasKeyword(card, "spAllPump");
           
           String parse = card.getKeyword().get(n).toString();
           card.removeIntrinsicKeyword(parse);
           
           String k[] = parse.split(":");
           
           final String Scope[] = k[1].split("/");
           
           final int NumAttack[] = {-1138};
           final String AttackX[] = {"none"};
           final int NumDefense[] = {-1138};
           final String DefenseX[] = {"none"};
           final String Keyword[] = {"none"};
           
           String ptk[] = k[2].split("/");
           
           if (ptk.length == 1)
              Keyword[0] = ptk[0];
           
           if (ptk.length >= 2)
           {
              if (ptk[0].matches("[\\+\\-][XY]"))
              {
                 String xy = card.getSVar(ptk[0].replaceAll("[\\+\\-]", ""));
                 if (xy.startsWith("Count$"))
                 {
                    String kk[] = xy.split("\\$");
                    AttackX[0] = kk[1];
                    
                    if (ptk[0].contains("-"))
                    {
                       if (AttackX[0].contains("/"))
                          AttackX[0] = AttackX[0].replace("/", "/Negative");
                       else
                          AttackX[0] += "/Negative";
                    }
                    
                 }
              }
              else if (ptk[0].matches("[\\+\\-][0-9]"))
                 NumAttack[0] = Integer.parseInt(ptk[0].replace("+", ""));
              
              if (ptk[1].matches("[\\+\\-][XY]"))
              {
                 String xy = card.getSVar(ptk[1].replaceAll("[\\+\\-]", ""));
                 if (xy.startsWith("Count$"))
                 {
                    String kk[] = xy.split("\\$");
                    DefenseX[0] = kk[1];
                    
                    if (ptk[0].contains("-"))
                    {
                       if (DefenseX[0].contains("/"))
                          DefenseX[0] = DefenseX[0].replace("/", "/Negative");
                       else
                          DefenseX[0] += "/Negative";
                    }
                    
                 }
              }
              else if (ptk[1].matches("[\\+\\-][0-9]"))
                 NumDefense[0] = Integer.parseInt(ptk[1].replace("+", ""));
           }
           
           if (ptk.length == 3)
              Keyword[0] = ptk[2];
           
           final String DrawBack[] = {"none"};
           final String spDesc[] = {"none"};
           final String stDesc[] = {"none"};
           
           if (k.length > 3)
           {
              if (k[3].contains("Drawback$"))
              {
                 String kk[] = k[3].split("\\$");
                 DrawBack[0] = kk[1];
                 if (k.length > 4) spDesc[0] = k[4];
                 if (k.length > 5) stDesc[0] = k[5];
              }
              else
              {
                 if (k.length > 3) spDesc[0] = k[3];
                 if (k.length > 4) stDesc[0] = k[4];
              }
           }
           
           SpellAbility spAllPump = new Spell(card)
           {
              private static final long serialVersionUID = 837472987492L;
              
                private int getNumAttack() {
                    if(NumAttack[0] != -1138) return NumAttack[0];
                   
                    if(!AttackX[0].equals("none")) return CardFactoryUtil.xCount(card, AttackX[0]);
                   
                    return 0;
                }
               
                private int getNumDefense() {
                    if(NumDefense[0] != -1138) return NumDefense[0];
                   
                    if(!DefenseX[0].equals("none")) return CardFactoryUtil.xCount(card, DefenseX[0]);
                   
                    return 0;
                }
               
                private int getNumKeyword()
                {
                   if (!Keyword[0].equals("none"))
                      return Keyword[0].split(" & ").length;
                   else return 0;
                }
               
                private CardList getScopeList()
                {
                   CardList l = new CardList();
                   
                   if (Scope[0].contains("YouCtrl"))
                      l.addAll(AllZone.getZone(Constant.Zone.Play, card.getController()).getCards());
                   
                   if (Scope[0].contains("All")) {
                      l.addAll(AllZone.getZone(Constant.Zone.Play, Constant.Player.Human).getCards());
                      l.addAll(AllZone.getZone(Constant.Zone.Play, Constant.Player.Computer).getCards());
                   }
                   
                   String fc[] = {"Creature"};
                   l = l.getValidCards(fc);
                   
                   if (Scope.length > 1)
                   {
                      String v = Scope[1];
                      if (v.length() > 0)
                         l = l.getValidCards(v.split(","));
                   }
                   
                   return l;
                }

                public boolean canPlayAI()
                {
                   System.out.println("Phase - " + AllZone.Phase.getPhase());
                   String curPhase = AllZone.Phase.getPhase();
                   if (curPhase.equals(Constant.Phase.Main2))
                      return false;
               
                   CardList sl = getScopeList();
                   int NumScope = sl.size();
                   
                   int defense = getNumDefense();
                   int attack = getNumAttack();
                   int key = getNumKeyword();
                   int th = (attack + defense + key) / 2; // Benefit Threshold
                   
                   if (NumScope > th) // have enough creatures in play
                   {
                      Combat c = ComputerUtil.getAttackers();
                      if (c.getAttackers().length >= th) // have enough creatures that will attack
                      {
                         int ndead = 0;
                         for (int i=0; i<sl.size(); i++) // check to see if this will kill any creatures
                            if ((sl.get(i).getNetDefense() + defense) < 1)
                               ndead++;
                         if (!(ndead > (sl.size() / 2))) // don't kill more than half of the creatures
                               return true;
                      }
                   }
                   
                   return false;
                }
               
                public void resolve()
                {
                   final int attack = getNumAttack();
                   final int defense = getNumDefense();
                   
                   final CardList sl = getScopeList();
                   
                   System.out.println("Phase - " + AllZone.Phase.getPhase());
                   
                   final Command untilEOT = new Command()
                   {
                      private static final long serialVersionUID = 92848209484928L;
                      
                      public void execute()
                      {
                         for (int i=0; i<sl.size(); i++)
                         {
                            Card c = sl.get(i);
                            if (AllZone.GameAction.isCardInPlay(c))
                            {
                               c.addTempAttackBoost(-attack);
                               c.addTempDefenseBoost(-defense);
                               
                               if (!Keyword[0].equals("none"))
                               {
                                  String kws[] = Keyword[0].split(" & ");
                                  for (int j=0; j<kws.length; j++)
                                     c.removeExtrinsicKeyword(kws[j]);
                               }
                            }
                         }
                      }
                   }; // untilEOT command
                   
                   for (int i=0; i<sl.size(); i++)
                   {
                      Card c = sl.get(i);
                      
                      if (AllZone.GameAction.isCardInPlay(c))
                      {
                         c.addTempAttackBoost(attack);
                         c.addTempDefenseBoost(defense);
                         
                         if (!Keyword[0].equals("none"))
                         {
                            String kws[] = Keyword[0].split(" & ");
                            for (int j=0; j<kws.length; j++)
                               c.addExtrinsicKeyword(kws[j]);
                         }
                      }
                   }
                   
                   AllZone.EndOfTurn.addUntil(untilEOT);
                   
                   if (!DrawBack[0].equals("none"))
                      CardFactoryUtil.doDrawBack(DrawBack[0], 0, card.getController(), AllZone.GameAction.getOpponent(card.getController()), card.getController(), card, card);
                } // resolve
           }; // spAllPump
           
           spAllPump.setDescription(spDesc[0]);
           spAllPump.setStackDescription(stDesc[0]);
           
           card.clearSpellAbility();
           card.addSpellAbility(spAllPump);
           
           card.setSVar("PlayMain1", "TRUE");
           
            String bbCost = card.getSVar("Buyback");
            if (!bbCost.equals(""))
            {
               SpellAbility bbAllPump = spAllPump.copy();
               bbAllPump.setManaCost(CardUtil.addManaCosts(card.getManaCost(), bbCost));
               bbAllPump.setDescription("Buyback " + bbCost + "(You may pay an additional " + bbCost + " as you cast this spell. If you do, put this card into your hand as it resolves.)");
               bbAllPump.setIsBuyBackAbility(true);
                             
               card.addSpellAbility(bbAllPump);
            }

        }
Notice, I didn't bother to parse the keyword to build descriptions, pulling them from the keyword line works just as well.

CardFactoryUtil.doDrawback:
Code: Select all
        if(d[0].contains("UntapAll")) // 6/10
        {
           CardList ut = new CardList();
           if (d[0].contains("YouCtrl"))
              ut.addAll(AllZone.getZone(Constant.Zone.Play, dbPlayer).getCards());
           else if (d[0].contains("OppCtrl"))
              ut.addAll(AllZone.getZone(Constant.Zone.Play, Opp).getCards());
           else
           {
              ut.addAll(AllZone.getZone(Constant.Zone.Play, dbPlayer).getCards());
              ut.addAll(AllZone.getZone(Constant.Zone.Play, Opp).getCards());
           }
           if (d[0].contains("Type"))
           {
              String dd[] = d[0].split("\\.");
              ut = ut.getValidCards(dd);
           }
           
           for (int i=0; i<ut.size(); i++)
              ut.get(i).untap();
        }
cards.txt: (Most are just keyword conversions)
Code: Select all
Call to Glory
1 W
Instant
no text
spAllPump:YouCtrl/Samurai:+1/+1:Drawback$UntapAllTypeYouCtrl.Creature:Untap all creatures you control. Samurai creatures get +1/+1 until end of turn.:Call to Glory - Untap all creatures you control; Samurai get +1/+1 until EOT

Chorus of Woe
B
Sorcery
no text
spAllPump:YouCtrl:+1/+0:Creatures you control get +1/+0 until end of turn.:Chorus of Woe - Creatures get +1/+0 until EOT

Dance of Shadows
3 B B
Sorcery Arcane
no text
spAllPump:YouCtrl:+1/+0/Fear:Creatures you control get +1/+0 and gain fear until end of turn.:Dance of Shadows - Creatures get +1/+0 and gain fear until EOT

Desperate Charge
2 B
Sorcery
no text
spAllPump:YouCtrl:+2/+0:Creatures you control get +2/+0 until end of turn.:Desperate Charge - Creatures get +2/+0 until EOT

Flowstone Slide
X 2 R R
Sorcery
no text
spAllPump:All:+X/-X:All Creatures get +X/-X until end of turn.:Flowstone Slide - All creatures get +X/-X until EOT
SVar:X:Count$xPaid

Gleam of Resistance
4 W
Instant
no text
spAllPump:YouCtrl:+1/+2:Drawback$UntapAllTypeYouCtrl.Creature:Creatures you control get +1/+2 until end of turn. Untap those creatures.:Gleam of Resistance - Creatures get +1/+2 until EOT; untap those creatures
TypeCycling:Basic:1 W

Glorious Charge
1 W
Instant
no text
spAllPump:YouCtrl:+1/+1:Creatures you control get +1/+1 unti end of turn.:Glorious Charge - Creatures get +1/+1 until EOT

Haze of Rage
1 R
Sorcery
no text
spAllPump:YouCtrl:+1/+0:Creatures you control get +1/+0 until end of turn.:Haze of Rage - Creatures get +1/+0 until EOT
SVar:Buyback:2
Storm

Kjeldoran War Cry
1 W
Instant
no text
spAllPump:YouCtrl:+X/+X:Creatures you control get +X/+X until end of turn, where X is 1 plus the number of cards named Kjeldoran War Cry in all graveyards.:Kjeldoran War Cry - Creatures get +X/+X until EOT
SVar:X:Count$NamedInAllYards.Kjeldoran War Cry/Plus.1

Magnify
G
Instant
no text
spAllPump:All:+1/+1:All creatures get +1/+1 until end of turn.:Magnify - Creatures get +1/+1 until EOT

Nocturnal Raid
2 B B
Instant
no text
spAllPump:All/Black:+2/+0:Black creatures get +2/+0 until end of turn.:Nocturnal Raid - Black creatures get +2/+0 until EOT

Overrun
2 G G G
Sorcery
no text
spAllPump:YouCtrl:+3/+3/Trample:Creatures you control get +3/+3 and gain trample until end of turn.:Overrun - Creatures you control get +3/+3 and gain trample until EOT

Path of Anger's Flame
2 R
Instant - Arcane
no text
spAllPump:YouCtrl:+2/+0:Creatures you control get +2/+0 until end of turn.:Path of Anger's Flame - Creatures get +2/+0 until EOT

Righteous Charge
1 W W
Sorcery
no text
spAllPump:YouCtrl:+2/+2:Creatures you control get +2/+2 until end of turn.:Righteous Charge - Creatures get +2/+2 until EOT

Scare Tactics
B
Instant
no text
spAllPump:YouCtrl:+1/+0:Creatures you control get +1/+0 until end of turn.:Scare Tactics - Creatures get +1/+0 until EOT

Shield Wall
1 W
Instant
no text
spAllPump:YouCtrl:+0/+2:Creatures you control get +0/+2 until end of turn.:Shield Wall - Creatures get +0/+2 until EOT

Solidarity
3 W
Instant
no text
spAllPump:YouCtrl:+0/+5:Creatures you control get +0/+5 until end of turn.:Solidarity - Creatures get +0/+5 until EOT

Steadfastness
1 W
Sorcery
no text
spAllPump:YouCtrl:+0/+3:Creatures you control get +0/+3 until end of turn.:Steadfastness - Creatures get +0/+3 until EOT

Titanic Ultimatum
R R G G G W W
Sorcery
no text
spAllPump:YouCtrl:+5/+5/First Strike & Lifelink & Trample:Until end of turn, creatures you control get +5/+5 and gain first strike, lifelink and trample.:Titanic Ultimatum - Creatures get +5/+5 and gain first strike, lifelink and trample until EOT

Tromp the Domains
5 G
Sorcery
no text
spAllPump:YouCtrl:+X/+X/Trample:Domain - Until end of turn, creatures you control gain trample and get +1/+1 for each basic land type among lands you control.:Tromp the Domains - Creatures get +X/+X and gain trample until EOT
SVar:X:Count$Domain

Valorus Charge
1 W W
Sorcery
no text
spAllPump:All/White:+2/+0:White creatures get +1/+0 until end of turn.:Valorus Charge - Creatures get +1/+0 until EOT

Violent Outburst
1 R G
Instant
no text
spAllPump:YouCtrl:+1/+0:Creatures you control get +1/+0 until end of turn.:Violent Outburst - Creatures get +1/+0 until EOT
Cascade

Virtuous Charge
2 W
Sorcery
no text
spAllPump:YouCtrl:+1/+1:Creatures you control get +1/+1 until end of turn.:Virtuous Charge - Creatures get +1/+1 until EOT

Vitalizing Wind
8 G
Instant
no text
spAllPump:YouCtrl:+7/+7:Creatures you control get +7/+7 until end of turn.:Vitalizing Wind - Creatures get +7/+7 until EOT

Warrior's Charge
2 W
Sorcery
no text
spAllPump:YouCtrl:+1/+1:Creatures you control get +1/+1 until end of turn.:Warrior's Charge - Creatures get +1/+1 until EOT

Warrior's Honor
2 W
Instant
no text
spAllPump:YouCtrl:+1/+1:Creatures you control get +1/+1 until end of turn.:Warrior's Honor - Creatures get +1/+1 until EOT

Burst of Speed
R
Sorcery
no text
spAllPump:YouCtrl:Haste:Creatures you control gain haste until end of turn.:Burst of Speed - Creatures gain haste until EOT

Crossbow Ambush
G
Instant
no text
spAllPump:YouCtrl:Reach:Creatures you control gain reach until end of turn.:Crossbow Ambush - Creatures gain reach until EOT

Magnetic Flux
2 U
Instant
no text
spAllPump:YouCtrl/Artifact:Flying:Artifact creatures you control gain flying until end of turn.:Magnetic Flux - Creatures gain flying until EOT

Nature's Cloak
2 G
Sorcery
no text
spAllPump:YouCtrl/Green:Forestwalk:Green creatures you control gain forestwalk until end of turn.:Nature's Cloak - Creatures gain forestwalk until EOT

Tortoise Formation
3 U
Instant
no text
spAllPump:YouCtrl:Shroud:Creatures you control gain shroud until end of turn.:Tortoise Formation - Creatures gain shroud until EOT
The code had already been committed along with the PlayMain1 cards tweak.
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: spAllPump keyword

Postby Chris H. » 05 Jun 2010, 15:53

Thank you Rob. :D

In a quick test I thought that I saw Flowstone Slide giving my creatures +X/+X rather than +X/-X until end of turn. I cast it with several small and cheap creatures in play and I expected this spell to kill my creatures. :o
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: spAllPump keyword

Postby Chris H. » 05 Jun 2010, 17:54

OK, four of these cards are new and do not require commenting out a block of code. Haze of Rage and Titanic Ultimatum are individual code blocks and these can be easily commented out. 8)

The rest of your list come from a large code block of spells which appear right below the links to different CardFactories. This code block is fairly long and complicated.

There are five spells included in this code block which are not in your list. Four of these cards are keyword buffs with no P/T buff. Can your keyword handle these four spells?:

Brave the Elements
Burst of Speed
Nature's Cloak
Tortoise Formation

There is one last spell in this block that could be un-keywordable and I guess that the small amount of code for this one card would have to be placed in it's own separate code block. This card is:

Resuscitate

Hmmm, just ran a test with this card and the current code and the regenerateMe is displayed but you can not activate the ability. After looking at the code I expected that this ability would not be active. :-k

Well, if the four cards above can be handled by your keyword, then we can convert these to keyword and I can give Resuscitate a note stating that this card does not work. The large code block can then be commented out and we will not need to edit this code section.
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: spAllPump keyword

Postby Rob Cashwalker » 05 Jun 2010, 22:44

If Flowstone Slide isn't working, it might just be a typo.

Brave the Elements is not one of my original cards for that section it would need to become its own code. Burst of Speed, Nature's Cloak and Tortoise Formation are on the list I put up.

Resuscitate would need to be its own code, there's no easy way to just dynamically add regenerate by keyword. At least not yet, it's one of those things that we keep dreaming about - a keyword factory....
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: spAllPump keyword

Postby Rob Cashwalker » 07 Jun 2010, 04:16

Yep, it was a typo in the code:
Code: Select all
                    DefenseX[0] = kk[1];
                    
                    if (ptk[0].contains("-"))
                    {
                       if (DefenseX[0].contains("/"))
That ptk[0] should be ptk[1].

I fixed and committed the change.
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: spAllPump keyword

Postby Chris H. » 07 Jun 2010, 10:22

Thank you Rob. :) I noticed something strange about Path of Anger's Flame and Kjeldoran War Cry. These card's spell description is not being displayed in the card detail panel. The cards.txt entries look OK and these spells will give a boost when cast.
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: spAllPump keyword

Postby Rob Cashwalker » 07 Jun 2010, 13:44

Maybe they have some original code hidden somewhere?
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: spAllPump keyword

Postby Chris H. » 07 Jun 2010, 15:02

Rob Cashwalker wrote:Maybe they have some original code hidden somewhere?
`
Yep, that explains it ... both of these cards also have a separate card object farther down in CardFactory. Time to pull out the mop and bucket from the broom closet again. :D
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: spAllPump keyword

Postby slapshot5 » 12 Oct 2010, 04:02

In trying to add Holy Light, I used the following keyword:

Code: Select all
K:spAllPump:All/nonWhite:-1/-1:Nonwhite creatures get -1/-1 until end of turn.:Holy Light - nonwhite creatures get -1/-1 until EOT.
This was based upon Nocturnal Raid, given above. However, I had to change All/nonWhite to All.nonWhite for it to work as expected.

Code: Select all
K:spAllPump:All.nonWhite:-1/-1:Nonwhite creatures get -1/-1 until end of turn.:Holy Light - nonwhite creatures get -1/-1 until EOT.
Is this expected, or is the parsing wrong?

-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: spAllPump keyword

Postby Sloth » 12 Oct 2010, 05:34

slapshot5 wrote:In trying to add Holy Light, I used the following keyword:

Code: Select all
K:spAllPump:All/nonWhite:-1/-1:Nonwhite creatures get -1/-1 until end of turn.:Holy Light - nonwhite creatures get -1/-1 until EOT.
This was based upon Nocturnal Raid, given above. However, I had to change All/nonWhite to All.nonWhite for it to work as expected.

Code: Select all
K:spAllPump:All.nonWhite:-1/-1:Nonwhite creatures get -1/-1 until end of turn.:Holy Light - nonwhite creatures get -1/-1 until EOT.
Is this expected, or is the parsing wrong?

-slapshot5
If I'm reading the parser correctly Nocturnal Raid is also wrong and it should be:

Code: Select all
K:spAllPump:All/Creature.nonWhite:-1/-1:Nonwhite creatures get -1/-1 until end of turn.:Holy Light - nonwhite creatures get -1/-1 until EOT.
Creature can also be Card or Permanent, since Creatures get checked already.

Remember though, that the AI can't handle negative spAllPump spells at the moment. Maybe a Curse option can be written?
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times


Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 47 guests


Who is online

In total there are 47 users online :: 0 registered, 0 hidden and 47 guests (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 47 guests

Login Form