It is currently 09 Sep 2025, 17:42
   
Text Size

Code from slapshot5

Post MTG Forge Related Programming Questions Here

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

Re: Code from slapshot5

Postby slapshot5 » 09 Apr 2010, 05:22

This is Gaea's Avenger from Antiquities

GameActionUtil.java

Code: Select all
public static Command Gaeas_Avenger                   = new Command() {
                                                          private static final long serialVersionUID = 1987511098173387864L;
                                                         
                                                          public void execute() {
                                                              // get all creatures
                                                              CardList list = new CardList();
                                                              list.addAll(AllZone.Human_Play.getCards());
                                                              list.addAll(AllZone.Computer_Play.getCards());
                                                              list = list.getName("Gaea's Avenger");
                                                             
                                                              for(int i = 0; i < list.size(); i++) {
                                                                  Card c = list.get(i);
                                                                  c.setBaseAttack(countOppArtifacts(c)+1);
                                                                  c.setBaseDefense(c.getBaseAttack());
                                                              }
                                                             
                                                          }// execute()
                                                         
                                                          private int countOppArtifacts(Card c) {
                                                              PlayerZone play = AllZone.getZone(Constant.Zone.Play, AllZone.GameAction.getOpponent(c.getController()));
                                                              CardList artifacts = new CardList(play.getCards());
                                                              artifacts = artifacts.getType("Artifact");
                                                              return artifacts.size();
                                                          }
                                                      };
add this to StaticEffects.java:
Code: Select all
cardToEffectsList.put("Gaea's Avenger", new String[] {"Gaeas_Avenger"});
add this to GameActionUtil.commands:
Code: Select all
commands.put("Gaeas_Avenger", Gaeas_Avenger);
cards.txt:

Code: Select all
Gaea's Avenger
1 G G
Creature Treefolk
Gaea's Avenger's power and toughness are each equal to 1 plus the number of artifacts your opponents control.
1/1
URL:
Code: Select all
http://www.wizards.com/global/images/magic/general/gaeas_avenger.jpg
-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: Code from slapshot5

Postby slapshot5 » 10 Apr 2010, 05:11

First pass code for Diamond Valley from Arabian Nights:

CardFactory_Lands.java:
Code: Select all
//*************** START *********** START **************************
        else if(cardName.equals("Diamond Valley")) {
            final Ability_Tap ability = new Ability_Tap(card, "0") {
               
                private static final long serialVersionUID = -6589125674356046586L;
               
                @Override
                public boolean canPlayAI() {
                    CardList list = new CardList(AllZone.Computer_Play.getCards());
                    list = list.filter(new CardListFilter() {
                        public boolean addCard(Card c) {
                            return c.isCreature();
                        }
                    });
                   
                    if(list.size() > 0 && AllZone.Computer_Life.getLife() < 5 ) setTargetCard(CardFactoryUtil.AI_getBestCreature(list, card));
                   
                    return list.size() > 0;
                }
               
                @Override
                public void resolve() {
                    Card c = getTargetCard();
                   
                    if(c != null) {
                        if(CardFactoryUtil.canTarget(card, c) && c.isCreature() ) {
                           AllZone.GameAction.addLife(c.getController(),c.getNetDefense());
                           AllZone.GameAction.sacrifice(c);
                        }
                    }
                }
            };
           
            Input runtime = new Input() {
               
                private static final long serialVersionUID = -7649177692384343204L;
               
                @Override
                public void showMessage() {
                    CardList choice = new CardList();
                    final String player = AllZone.Phase.getActivePlayer();
                    PlayerZone play = AllZone.getZone(Constant.Zone.Play, player);
                    choice.addAll(play.getCards());

                    choice = choice.getType("Creature");
                    choice = choice.filter(new CardListFilter() {
                        public boolean addCard(Card c) {
                            return (c.isCreature());
                        }
                    });
                   
                    stopSetNext(CardFactoryUtil.input_targetSpecific(ability, choice,
                            "Select target creature:", true, false));
                }
            };

            card.addSpellAbility(ability);
            ability.setBeforePayMana(runtime);
           
        }//*************** END ************ END **************************
cards.txt:
Code: Select all
Diamond Valley
no cost
Land
tap: Sacrifice a creature: You gain life equal to the sacrificed creature's toughness.
URL:
Code: Select all
http://www.wizards.com/global/images/magic/general/diamond_valley.jpg
Please review.

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

Re: Code from slapshot5

Postby slapshot5 » 10 Apr 2010, 06:10

This is People of the Woods from The Dark:

GameActionUtil.java

Code: Select all
public static Command People_of_the_Woods                   = new Command() {
                                                          private static final long serialVersionUID = 1987554325573387864L;
                                                         
                                                          public void execute() {
                                                              // get all creatures
                                                              CardList list = new CardList();
                                                              list.addAll(AllZone.Human_Play.getCards());
                                                              list.addAll(AllZone.Computer_Play.getCards());
                                                              list = list.getName("People of the Woods");
                                                             
                                                              for(int i = 0; i < list.size(); i++) {
                                                                  Card c = list.get(i);
                                                                  c.setBaseAttack(1);
                                                                  c.setBaseDefense(countForests(c));
                                                              }
                                                             
                                                          }// execute()
                                                         
                                                          private int countForests(Card c) {
                                                              PlayerZone play = AllZone.getZone(
                                                                      Constant.Zone.Play, c.getController());
                                                              CardList forests = new CardList(play.getCards());
                                                              forests = forests.getType("Forest");
                                                              return forests.size();
                                                          }
                                                      }; 
add this to StaticEffects.java:
Code: Select all
cardToEffectsList.put("People of the Woods", new String[] {"People_of_the_Woods"});
add this to GameActionUtil.commands:
Code: Select all
commands.put("People_of_the_Woods", People_of_the_Woods);
cards.txt:

Code: Select all
People of the Woods
G G
Creature Human
People of the Woods's toughness is equal to the number of Forests you control.
1/0
URL:
Code: Select all
http://www.wizards.com/global/images/magic/general/people_of_the_woods.jpg
-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: Code from slapshot5

Postby slapshot5 » 10 Apr 2010, 19:09

Here is Jandor's Saddlebags

CardFactory.java:
Code: Select all
//*****************************START*******************************
        if(cardName.equals("Jandor's Saddlebags")) {
           /* Assuing the Rules state that this can target an untapped card,
            * but it won't do anything useful
            *
            * This would bring the ruling in line with Icy Manipulator
            * */
           
           final Ability_Tap ability = new Ability_Tap(card, "3") {
         private static final long serialVersionUID = 6349074098650621348L;
         public boolean canPlayAI() {
                 return false;
              }
              public void chooseTargetAI() {
                 //setTargetCard(c);
              }//chooseTargetAI()
              public void resolve() {
                 if(AllZone.GameAction.isCardInPlay(getTargetCard())) {
                    getTargetCard().untap();
                 }
              }
           };//SpellAbility
           
           card.addSpellAbility(ability);
           ability.setDescription("3, tap: Untap target creature.");
           ability.setBeforePayMana(CardFactoryUtil.input_targetType(ability, "Creature"));
        }//Jandor's Saddlebags
        //****************END*******END***********************
cards.txt:
Code: Select all
Jandor's Saddlebags
2
Artifact
no text
URL:
Code: Select all
http://www.wizards.com/global/images/magic/general/jandors_saddlebags.jpg
-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: Code from slapshot5

Postby jim » 10 Apr 2010, 23:34

slapshot5 wrote:First pass at City of Brass

<snip>
(Also, note this provides a general mechanism for cards that have a side effect when tapped.)

-slapshot5
I like your implementation, and the side effect mechanism. When I played with it, I found that when I select a spell to cast and tap City of Brass to pay the mana cost, the point of damage goes on the stack right away. If you then hit 'cancel' to stop casting the spell you still have taken the point of damage, even though City of Brass untaps. If you tap it again to pay the cost of a different spell you'll take another point of damage.

One way to do avoid this would be to defer side effect execution until the spell casting is done. It's hard to do because there are a couple of layers involved (Input_PayManaCostUtil.tapCard calls GameAction.playSpellAbility which calls tap()). We could have Input_PayManaCostUtil call a new routine called GameAction.playSpellAbilitySideEffectFree which calls tapSideEffectFree(), and then have Input_PayManaCost store a list of cards that were tapped. It would then call executeTapSideEffects on these cards afterward. Does anyone else have a better idea how to deal with this?
jim
 
Posts: 46
Joined: 19 Feb 2010, 01:46
Location: Sunny New England
Has thanked: 0 time
Been thanked: 0 time

Re: Code from slapshot5

Postby slapshot5 » 11 Apr 2010, 04:52

I think I have Winter Orb working:

Add this method to Input_Untap.java:
Code: Select all
    private boolean isWinterOrbInEffect() {
       CardList all = new CardList();
        all.addAll(AllZone.Human_Play.getCards());
        all.addAll(AllZone.Computer_Play.getCards());
        all = all.filter(new CardListFilter() {
            public boolean addCard(Card c) {
                return c.getName().equals("Winter Orb");
            }
        });
       
        //if multiple Winter Orbs, check that at least 1 of them is untapped
        for( int i = 0; i < all.size(); i++ ) {
           if( all.get(i).isUntapped() ) {
              return true;
           }
        }
        return false;
      }
Modify doUntap() in Input_Untap.java to look like this:
Code: Select all
    private void doUntap()
    {
       PlayerZone p = AllZone.getZone(Constant.Zone.Play, AllZone.Phase.getActivePlayer());
        CardList list = new CardList(p.getCards());
        list = list.filter(new CardListFilter()
        {
           public boolean addCard(Card c)
           {
              if( isMarbleTitanInPlay() && isWinterOrbInEffect() ) {
                 return !c.isLand() && c.getNetAttack() < 3;
              }
              else if( isWinterOrbInEffect() ) {
                 return !c.isLand();
              }
              else if (isMarbleTitanInPlay()) {
                 return c.getNetAttack() < 3;
              }
                 
              return true;
           }
        });
       
        for(Card c : list) {
            if(!c.getKeyword().contains("This card doesn't untap during your untap step.")
                    && !c.getKeyword().contains("This card doesn't untap during your next untap step.")) c.untap();
            else c.removeExtrinsicKeyword("This card doesn't untap during your next untap step.");
   
       }
        if( isWinterOrbInEffect() ) {
           if( AllZone.Phase.getActivePlayer().equals(Constant.Player.Computer) ) {
              //search for lands the computer has and only untap 1
              CardList landList = new CardList(p.getCards());
                landList = landList.filter(new CardListFilter()
                {
                   public boolean addCard(Card c)
                   {
                      return c.isLand() && c.isTapped();
                   }
                });
                if( landList.size() > 0 ) {
                   landList.get(0).untap();
                }
           }
           else {
              Input target = new Input() {
                 private static final long serialVersionUID = 6653677835629939465L;
                 public void showMessage() {
                    AllZone.Display.showMessage("Winter Orb - Select one tapped land to untap");
                    ButtonUtil.enableOnlyCancel();
                 }
                 public void selectButtonCancel() {stop();}
                 public void selectCard(Card c, PlayerZone zone) {
                    if(c.isLand() && zone.is(Constant.Zone.Play) && c.isTapped()) {
                       c.untap();
                       stop();
                    }
                 }//selectCard()
              };//Input
              CardList landList = new CardList(p.getCards());
                landList = landList.filter(new CardListFilter()
                {
                   public boolean addCard(Card c)
                   {
                      return c.isLand() && c.isTapped();
                   }
                });
                if( landList.size() > 0 ) {
                   AllZone.InputControl.setInput(target);
                }
              
           }
        }
    }//end doUntap
 
Then, cards.txt:
Code: Select all
Winter Orb
2
Artifact
As long as Winter Orb is untapped, players can't untap more than one land during their untap steps.
URL:
Code: Select all
http://www.wizards.com/global/images/magic/general/winter_orb.jpg
This seems to be generally working for me. It does need more testing around multiple Winter Orbs in play with some of them tapped and some untapped. It should also be tested when combinations of Stasis / Marble Titan / Meekstone are in play.

I believe this is on the card request list.

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

Re: Code from slapshot5

Postby DennisBergkamp » 11 Apr 2010, 07:26

Very cool, Winter Orb is definitely a classic :)
I guess it didn't make it into the latest Beta (I just posted it), but that's probably a good thing: we can give it some more testing now.
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Code from slapshot5

Postby zerker2000 » 11 Apr 2010, 12:09

I'd say default Ability_Mana.undo() should have a line saying "if isTapAbility Stack.remove(tapTriggerAbility)".
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: Code from slapshot5

Postby jim » 11 Apr 2010, 12:27

zerker2000 wrote:I'd say default Ability_Mana.undo() should have a line saying "if isTapAbility Stack.remove(tapTriggerAbility)".
I like that. To do it, we'd need to have the tapTriggerAbility added to the cards when they are created, because otherwise Ability_Mana can't find them...but this is probably a good idea anyway.
jim
 
Posts: 46
Joined: 19 Feb 2010, 01:46
Location: Sunny New England
Has thanked: 0 time
Been thanked: 0 time

Re: Code from slapshot5

Postby zerker2000 » 12 Apr 2010, 05:11

Actually, I'm pretty sure the stack doesn't error out on remove(null), so that could just be added as a line to Card(and if it does, put a check in undo()).
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: Code from slapshot5

Postby jim » 12 Apr 2010, 09:53

zerker2000 wrote:Actually, I'm pretty sure the stack doesn't error out on remove(null), so that could just be added as a line to Card(and if it does, put a check in undo()).
You're right -- that will handle cards without tapTriggerAbilities. I was pointing out that in the present implementation neither the card nor the spell ability have a link back to the tapTriggerAbility (unless I missed something). It's created on-the-fly in the 'executeTapTrigger' routine. I think we need to change that so they're created in the CardFactories and added to the card, in order to have an 'undo' capability.
jim
 
Posts: 46
Joined: 19 Feb 2010, 01:46
Location: Sunny New England
Has thanked: 0 time
Been thanked: 0 time

Re: Code from slapshot5

Postby slapshot5 » 13 Apr 2010, 13:14

Here's Twiddle - pretty much the same as Icy Manipulator.

CardFactory.java:
Code: Select all
//*****************************START*******************************
       if(cardName.equals("Twiddle")) {
          
          final SpellAbility spell = new Spell(card) {
            private static final long serialVersionUID = 1710491641333972105L;
            public boolean canPlayAI() {
                return false;
             }
             public void chooseTargetAI() {
                //setTargetCard(c);
             }//chooseTargetAI()
             public void resolve() {
                /* The rules provide that tap/untap is chosed on resolution.
                 * I think this simple implementation will suffice for Forge.
                 */
                if(AllZone.GameAction.isCardInPlay(getTargetCard())) {
                   if(getTargetCard().isTapped()) {
                      getTargetCard().untap();
                   }
                   else {
                      getTargetCard().tap();
                   }
                }
             }
          };//SpellAbility
          spell.setBeforePayMana(CardFactoryUtil.input_targetType(spell, "Artifact;Creature;Land"));
          card.clearSpellAbility();
          card.addSpellAbility(spell);      
       }//end Twiddle
       //****************END*******END***********************
cards.txt:
Code: Select all
Twiddle
U
Instant
You may tap or untap target artifact, creature, or land.
URL:
Code: Select all
http://www.wizards.com/global/images/magic/general/twiddle.jpg
-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: Code from slapshot5

Postby slapshot5 » 13 Apr 2010, 14:30

I think I've got Zuran Orb working now for Human and AI:

It's been requested a few times in the other thread...

cards.txt
Code: Select all
Zuran Orb
0
Artifact
0: Sacrifice a land to gain 2 life.
CardFactory.java:
Code: Select all

        //*************** START *********** START **************************
        if(cardName.equals("Zuran Orb")) {
           final SpellAbility ability = new Ability_Activated(card,"0") {
              private static final long serialVersionUID = 6349074098650435648L;
              public boolean canPlayAI() {
                    if( CardFactoryUtil.getLandsInPlay(Constant.Player.Computer).size() > 0 ) {
                       if( AllZone.GameAction.getPlayerLife(Constant.Player.Computer).getLife() < 5 ) {
                          return true;
                       }
                       else {
                          return false;
                       }
                    }
                    else return false;
              }
              public void chooseTargetAI() {
                 Card target = null;
                    target = CardFactoryUtil.getWorstLand(Constant.Player.Computer);
                    setTargetCard(target);
              }//chooseTargetAI()
              public void resolve() {
                 AllZone.GameAction.getPlayerLife(card.getController()).addLife(2);
                 AllZone.GameAction.sacrifice(getTargetCard());
              }
           };//SpellAbility

           Input runtime = new Input() {
            private static final long serialVersionUID = -64941510699003443L;

            public void showMessage() {
                 ability.setStackDescription(card +" - Sacrifice a land to gain 2 life.");
                 PlayerZone play = AllZone.getZone(Constant.Zone.Play,card.getController());
                 CardList choice = new CardList(play.getCards());
                 choice  = choice.getType("Land");
                 stopSetNext(CardFactoryUtil.input_sacrifice(ability,choice,"Select a land to sacrifice."));
              }
           };

           ability.setStackDescription("Zuran Orb - Gain 2 life.");
           card.addSpellAbility(ability);
           ability.setBeforePayMana(runtime);
        }//*************** END ************ END **************************

Add these two functions to CardFactoryUtil.java:
Code: Select all
   
       /**
     * getWorstLand(String)
     *
     * This function finds the worst land a player has in play based on:
     * worst
     * 1. tapped, basic land
     * 2. tapped, non-basic land
     * 3. untapped, basic land
     * 4. untapped, non-basic land
     * best
     *
     * This is useful when the AI needs to find one of its lands to sacrifice
     *
     * @param player - Constant.Player.Human or Constant.Player.Computer
     * @return the worst land found based on the description above
     */
    public static Card getWorstLand(String player) {
       Card worstLand = null;
       CardList lands = CardFactoryUtil.getLandsInPlay(player);
        //first, check for tapped, basic lands
        for( int i = 0; i < lands.size(); i++ ) {
           Card tmp = lands.get(i);
           if(tmp.isTapped() && tmp.isBasicLand()) {
              worstLand = tmp;
           }
        }
        //next, check for tapped, non-basic lands
        if(worstLand == null) {
           for( int i = 0; i < lands.size(); i++ ) {
               Card tmp = lands.get(i);
               if(tmp.isTapped()) {
                  worstLand = tmp;
               }
            }
        }
        //next, untapped, basic lands
        if(worstLand == null) {
           for( int i = 0; i < lands.size(); i++ ) {
               Card tmp = lands.get(i);
               if(tmp.isUntapped() && tmp.isBasicLand()) {
                  worstLand = tmp;
               }
            }
        }
        //next, untapped, non-basic lands
        if(worstLand == null) {
           for( int i = 0; i < lands.size(); i++ ) {
               Card tmp = lands.get(i);
               if(tmp.isUntapped()) {
                  worstLand = tmp;
               }
            }
        }
        return worstLand;
    }//end getWorstLand
   
    /**
     * getLandsInPlay(String)
     *
     * This function returns a CardList of all lands that the given
     * player has in Constant.Zone.Play
     *
     * @param player - Constant.Player.Human or Constant.Player.Computer
     * @return a CardList of that players lands
     */
    public static CardList getLandsInPlay(String player) {
       PlayerZone compBattlezone = AllZone.getZone(Constant.Zone.Play, player);
        CardList list = new CardList(compBattlezone.getCards());
        list = list.filter(new CardListFilter() {
            public boolean addCard(Card c) {
                if(c.isLand()) return true;
                else return false;
            }
        });
        return list;
    }
URL:
Code: Select all
http://www.wizards.com/global/images/magic/general/zuran_orb.jpg
-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: Code from slapshot5

Postby slapshot5 » 13 Apr 2010, 14:51

This is Keldon Warlord

cards.txt:
Code: Select all
Keldon Warlord
2 R R
Creature Human Barbarian
Keldon Warlord's power and toughness are each equal to the number of non-Wall creatures you control.
1/1
GameActionUtil.java:
Code: Select all
commands.put("Keldon_Warlord", Keldon_Warlord);
Code: Select all
public static Command Keldon_Warlord                   = new Command() {
      private static final long serialVersionUID = 3804539422363462063L;

      public void execute() {
         // get all creatures
         CardList list = new CardList();
         list.addAll(AllZone.Human_Play.getCards());
         list.addAll(AllZone.Computer_Play.getCards());
         list = list.getName("Keldon Warlord");

         for(int i = 0; i < list.size(); i++) {
            Card c = list.get(i);
            c.setBaseAttack(countCreatures(c));
            c.setBaseDefense(c.getNetAttack());
         }

      }// execute()

      private int countCreatures(Card c) {
         PlayerZone play = AllZone.getZone(Constant.Zone.Play, c.getController());
         CardList creatures = new CardList(play.getCards());
         creatures = creatures.filter(new CardListFilter() {
                public boolean addCard(Card c) {
                    return c.isCreature() && !c.isWall();
                }
            });
         return creatures.size();
      }
   };

StaticEffects.java:
Code: Select all
cardToEffectsList.put("Keldon Warlord", new String[] {"Keldon_Warlord"});
Card.java:
Code: Select all
public boolean isWall() {
       return type.contains("Wall");
    }
URL:
Code: Select all
http://www.wizards.com/global/images/magic/general/keldon_warlord.jpg
-slapshot5
slapshot5
Programmer
 
Posts: 1391
Joined: 03 Jan 2010, 17:47
Location: Mac OS X
Has thanked: 25 times
Been thanked: 68 times

Re: Code from slapshot5

Postby Rob Cashwalker » 13 Apr 2010, 17:02

FYI: generally, you can use the CardFactoryUtil.xCount method to provide the same functionality as the usual custom count____ code you have used. It should be easy to add a "Non" clause.... It would be something like this:

xCount("TypeYouCtrl.NonWall")

Also, this sort of ability is very common, and you should be able to turn the code into a keyword.
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

PreviousNext

Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 71 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 71 users online :: 0 registered, 0 hidden and 71 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 71 guests

Login Form