It is currently 11 Sep 2025, 17:16
   
Text Size

ChangeZone - changing spell on the stack

Post MTG Forge Related Programming Questions Here

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

ChangeZone - changing spell on the stack

Postby moomarc » 18 Apr 2012, 00:39

I've just finished making it possible to use ChangeZone targeting spells on the stack. :mrgreen: Everything that was needed was almost there in CounterSpells, it just needed to be tweaked and ported. I've tested with Venser, Shaper Savant (as well as some other normal changeZone cards to make sure I didn't break it) and everything seems to work. At this stage its just for the human player, but I first wanted some experienced eyes checking what I've done so far to point out any errors and pitfalls. Here's a patch for AbilityFactoryChangeZone and the script for Venser:
Patch | Open
Code: Select all
### Eclipse Workspace Patch 1.0
#P Forge
Index: src/main/java/forge/card/abilityfactory/AbilityFactoryChangeZone.java
===================================================================
--- src/main/java/forge/card/abilityfactory/AbilityFactoryChangeZone.java   (revision 15148)
+++ src/main/java/forge/card/abilityfactory/AbilityFactoryChangeZone.java   (working copy)
@@ -1888,6 +1888,8 @@
      */
     private static void changeKnownOriginResolve(final AbilityFactory af, final SpellAbility sa) {
         ArrayList<Card> tgtCards;
+        ArrayList<SpellAbility> sas;
+
         final HashMap<String, String> params = af.getMapParams();
         final Target tgt = sa.getTarget();
         final Player player = sa.getActivatingPlayer();
@@ -1905,6 +1907,28 @@
             }
         }
 
+        // changing zones for spells on the stack
+        if (tgt != null) {
+            sas = tgt.getTargetSAs();
+        } else {
+            sas = AbilityFactory.getDefinedSpellAbilities(sa.getSourceCard(), params.get("Defined"), sa);
+        }
+
+        for (final SpellAbility tgtSA : sas) {
+            final Card tgtSACard = tgtSA.getSourceCard();
+
+            if (tgtSA.isSpell() && !CardFactoryUtil.isCounterable(tgtSACard)) {
+                continue;
+            }
+
+            final SpellAbilityStackInstance si = AllZone.getStack().getInstanceFromSpellAbility(tgtSA);
+            if (si == null) {
+                continue;
+            }
+
+            removeFromStack(tgtSA, sa, si);
+        } // End of change from stack
+
         final String remember = params.get("RememberChanged");
         final String imprint = params.get("Imprint");
 
@@ -2043,6 +2067,54 @@
         return ret;
     }
 
+    /**
+     * <p>
+     * removeFromStack.
+     * </p>
+     *
+     * @param tgtSA
+     *            a {@link forge.card.spellability.SpellAbility} object.
+     * @param srcSA
+     *            a {@link forge.card.spellability.SpellAbility} object.
+     * @param si
+     *            a {@link forge.card.spellability.SpellAbilityStackInstance}
+     *            object.
+     */
+    private static void removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si) {
+        AllZone.getStack().remove(si);
+
+        final AbilityFactory af = srcSA.getAbilityFactory();
+        final HashMap<String, String> params = af.getMapParams();
+
+        if (params.containsKey("Destination")) {
+            if (tgtSA.isAbility()) {
+                // Shouldn't be able to target Abilities on the stack
+            } else if (tgtSA.isFlashBackAbility())  {
+                Singletons.getModel().getGameAction().exile(tgtSA.getSourceCard());
+            } else if (params.get("Destination").equals("Graveyard")) {
+                Singletons.getModel().getGameAction().moveToGraveyard(tgtSA.getSourceCard());
+            } else if (params.get("Destination").equals("Exile")) {
+                Singletons.getModel().getGameAction().exile(tgtSA.getSourceCard());
+            } else if (params.get("Destination").equals("TopOfLibrary")) {
+                Singletons.getModel().getGameAction().moveToLibrary(tgtSA.getSourceCard());
+            } else if (params.get("Destination").equals("Hand")) {
+                Singletons.getModel().getGameAction().moveToHand(tgtSA.getSourceCard());
+            } else if (params.get("Destination").equals("BottomOfLibrary")) {
+                Singletons.getModel().getGameAction().moveToBottomOfLibrary(tgtSA.getSourceCard());
+            } else if (params.get("Destination").equals("ShuffleIntoLibrary")) {
+                Singletons.getModel().getGameAction().moveToBottomOfLibrary(tgtSA.getSourceCard());
+                tgtSA.getSourceCard().getController().shuffle();
+            } else {
+                throw new IllegalArgumentException("AbilityFactory_ChangeZone: Invalid Destination argument for card "
+                        + srcSA.getSourceCard().getName());
+            }
+
+            if (!tgtSA.isAbility()) {
+                System.out.println("Moving spell to " + params.get("Destination"));
+            }
+        }
+    }
+
     // *************************************************************************************
     // ************************** ChangeZoneAll
     // ********************************************
Venser, Shaper Savant | Open
Name:Venser, Shaper Savant
ManaCost:2 U U
Types:Legendary Creature Human Wizard
Text:no text
PT:2/2
K:Flash
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ ChooseTgtMode | TriggerDescription$ When CARDNAME enters the battlefield, return target spell or permanent to its owner's hand.
SVar:ChooseTgtMode:AB$ Charm | Cost$ 0 | Defined$ You | Choices$ BounceSpell,BouncePermanent
SVar:BounceSpell:DB$ChangeZone | TargetType$ Spell | ValidTgts$ Card | TgtZone$ Stack | Origin$ Stack | Destination$ Hand | SpellDescription$ Return target spell to its owner's hand.
SVar:BouncePermanent:DB$ChangeZone | ValidTgts$ Permanent | TgtPrompt$ Select target permanent | Origin$ Battlefield | Destination$ Hand | SpellDescription$ Return target permanent to its owner's hand.
SVar:RemAIDeck:True
SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/venser_shaper_savant.jpg
SetInfo:FUT|Rare|http://magiccards.info/scans/en/fut/46.jpg
Oracle:Flash (You may cast this spell any time you could cast an instant.)\nWhen Venser, Shaper Savant enters the battlefield, return target spell or permanent to its owner's hand.
End
If its almost right I'll make any changes you suggest, then I'll need to add something for the AI (I will need help there 8-[ ). The script probably needs to be tweaked slightly too because you the can currently cancel out of the triggered ability. Probably just a mandatory true flag or two needed.

EDIT: Of course it could be completely wrong and painful to trained eyes in which case feel free to tell me to just scratch the idea.
-Marc
User avatar
moomarc
Pixel Commander
 
Posts: 2091
Joined: 04 Jun 2010, 15:22
Location: Johannesburg, South Africa
Has thanked: 371 times
Been thanked: 372 times

Re: ChangeZone - changing spell on the stack

Postby Sloth » 18 Apr 2012, 05:31

No objections. And don't spend too much time on the AI, i don't think Venser will ever be played better than an overpriced Man-o'-War.
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: ChangeZone - changing spell on the stack

Postby moomarc » 18 Apr 2012, 08:56

Sloth wrote:No objections. And don't spend too much time on the AI, i don't think Venser will ever be played better than an overpriced Man-o'-War.
Well it does that straight out the box without adding anything to AI (maybe its for that very reason). I haven't been able to make the AI even try target a spell on the stack so I guess its all fine then. Maybe just have a final look when I commit to see if there's something that needs to be there just in case.

I'm going to try Mindbreak Trap as well seeing as its the only other card so far that I can see needs this. Maybe that card will show up some shortfalls before I commit.
-Marc
User avatar
moomarc
Pixel Commander
 
Posts: 2091
Joined: 04 Jun 2010, 15:22
Location: Johannesburg, South Africa
Has thanked: 371 times
Been thanked: 372 times

Re: ChangeZone - changing spell on the stack

Postby moomarc » 18 Apr 2012, 11:02

I've just committed the stuff I've done for this. Sadly I couldn't add Mindbreak Trap because there seems to be a problem with TargetsMin and TargetsMax when targeting things on the stack. I first came across this when I tried to script Double Negative.

Venser is good to go though (and I decided he's not an overcosted Man-o'-War. You pay the extra 1 for targeting permanents instead of just creatures. :D ) You'll only be given the option of targeting a spell on the stack if there is actually one there. Unfortunately it can still be cancelled out of when targeting a spell on the stack because of the issue I mentioned above. I figured that if the player decides to use this exploit though, they just suck :P )
-Marc
User avatar
moomarc
Pixel Commander
 
Posts: 2091
Joined: 04 Jun 2010, 15:22
Location: Johannesburg, South Africa
Has thanked: 371 times
Been thanked: 372 times


Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 39 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 39 users online :: 0 registered, 0 hidden and 39 guests (based on users active over the past 10 minutes)
Most users ever online was 7967 on 09 Sep 2025, 23:08

Users browsing this forum: No registered users and 39 guests

Login Form